<!--
  ~ Copyright 2010 the original author or authors.
  ~
  ~ 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.
  -->

<chapter id="idea_plugin">
    <title>The Idea Plugin</title>

    <para>The Idea plugin generates files that are used by <ulink url="http://www.jetbrains.com/idea/">IntelliJ Idea IDE</ulink>, thus
        making it possible to open the project from Idea (<guimenuitem>File</guimenuitem> - <guimenuitem>Open Project</guimenuitem>).
        Both external (including associated source and javadoc files) and project dependencies are considered.</para>

    <para>The Idea plugin will create different content depending on what other plugins being used.</para>
    
<section>
        <title>Usage</title>
    <para>To use the Idea plugin, include in your build script:</para>
    <sample id="useIdeaPlugin" dir="idea" title="Using the Idea plugin">
        <sourcefile file="build.gradle" snippet="use-plugin"/>
    </sample>
    <para>One focus of the Idea tasks is to be open to customizations. They provide a couple of hooks to add or remove content from
        the generated files.
</para>
    </section>
    <section>
        <title>Tasks</title>

        <para>The Idea plugin adds the tasks shown below to a project.</para>

        <table id='ideatasks'>
            <title>Idea plugin - Tasks</title>
            <thead>
                <tr>
                    <td>Task name</td>
                    <td>Depends on</td>
                    <td>Type</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tr>
                <td>
                    <literal>idea</literal>
                </td>
                <td><literal>ideaProject</literal>, <literal>ideaModule</literal>, <literal>ideaWorkspace</literal></td>
                <td><literal>-</literal></td>
                <td>Generates all the idea configuration files</td>
            </tr>
            <tr>
                <td>
                    <literal>cleanIdea</literal>
                </td>
                <td>
                    <literal>cleanIdeaProject</literal>, <literal>cleanIdeaModule</literal>, <literal>cleanIdeaWorkspace</literal>
                </td>
                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
                <td>Removes all idea configuration files</td>
            </tr>
            <tr>
                <td>
                    <literal>cleanIdeaProject</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
                <td>Removes the idea project file</td>
            </tr>
            <tr>
                <td>
                    <literal>cleanIdeaModule</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
                <td>Removes the idea module file</td>
            </tr>
            <tr>
                <td>
                    <literal>cleanIdeaWorkspace</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
                <td>Removes the idea workspace file</td>
            </tr>
            <tr>
                <td>
                    <literal>ideaProject</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.plugins.idea.IdeaProject" lang="groovy"/></td>
                <td>Generates the <filename>.ipr</filename> file. The Idea plugin adds this task only to the root project.</td>
            </tr>
            <tr>
                <td>
                    <literal>ideaModule</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.plugins.idea.IdeaModule" lang="groovy"/></td>
                <td>Generates the <filename>.iml</filename> file</td>
            </tr>
            <tr>
                <td>
                    <literal>ideaWorkspace</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.plugins.idea.IdeaWorkspace" lang="groovy"/></td>
                <td>Generates the <filename>.iws</filename> file</td>
            </tr>
        </table>

        <table id='idea-module'>
            <title>IdeaModule task</title>
            <thead>
                <tr>
                    <td>Property</td>
                    <td>Type</td>
                    <td>Default Value</td>
                    <td>Default Value with Java Plugin</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tr>
                <td>
                    <literal>moduleDir</literal>
                </td>
                <td>File</td>
                <td><literal>projectDir</literal></td>
                <td><literal>projectDir</literal></td>
                <td>The content root directory of the module. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>outputFile</literal>
                </td>
                <td>File</td>
                <td><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable><literal>.iml</literal></td>
                <td>-</td>
                <td>The iml file. Used to look for existing files and as the target for generation. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>sourceDirs</literal>
                </td>
                <td>Set&lt;File&gt;</td>
                <td>empty set</td>
                <td>The source dirs of <literal>sourceSets.main</literal></td>
                <td>The dirs containing the production sources. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>testSourceDirs</literal>
                </td>
                <td>Set&lt;File&gt;</td>
                <td>empty set</td>
                <td>The source dirs of <literal>sourceSets.test</literal></td>
                <td>The dirs containing the test sources. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>excludeDirs</literal>
                </td>
                <td>Set&lt;File&gt;</td>
                <td>empty set</td>
                <td>-</td>
                <td>The dirs to be excluded by idea. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>outputDir</literal>
                </td>
                <td>File</td>
                <td>null</td>
                <td><literal>sourceSets.main.classesDir</literal></td>
                <td>The idea output dir for the production sources. If null no entry for output dirs is created.</td>
            </tr>
            <tr>
                <td>
                    <literal>testOutputDir</literal>
                </td>
                <td>File</td>
                <td>null</td>
                <td><literal>sourceSets.test.classesDir</literal></td>
                <td>The idea output dir for the test sources. If null no entry for test output dirs is created.</td>
            </tr>
            <tr>
                <td>
                    <literal>javaVersion</literal>
                </td>
                <td>String</td>
                <td>null</td>
                <td>-</td>
                <td>If this is null the value of the existing or default ipr XML (inherited) is used. If it is set
                to <literal>inherited</literal>, the project SDK is used. Otherwise the SDK for the corresponding
                value of java version is used for this module</td>
            </tr>
            <tr>
                <td>
                    <literal>downloadSources</literal>
                </td>
                <td>boolean</td>
                <td>true</td>
                <td>-</td>
                <td>Whether to download and add source jars associated with the dependency jars.</td>
            </tr>
            <tr>
                <td>
                    <literal>downloadJavadoc</literal>
                </td>
                <td>boolean</td>
                <td>false</td>
                <td>-</td>
                <td>Whether to download and add javadoc jars associated with the dependency jars. </td>
            </tr>
            <tr>
                <td>
                    <literal>scopes</literal>
                </td>
                <td>Map</td>
                <td>[:]</td>
                <td>COMPILE(compile), RUNTIME(runtime - compile), TEST(testRuntime - runtime)</td>
                <td>The keys of this map are the Idea scopes. Each key points to another map that has two keys, plus and minus.
                    The values of those keys are sets of <link linkend="sub:configurations">Configuration</link> objects. The files of the
                    plus configurations are added minus the files from the minus configurations. </td>
                </tr>
            <tr>
                <td>
                    <literal>gradleCacheVariable</literal>
                </td>
                <td>String</td>
                <td>null</td>
                <td>-</td>
                <td>If this variable is set, dependencies in the existing iml file will be parsed for this variable. If they use it,
                    it will be replaced with a path that has the $MODULE_DIR$ variable as a root and
                    then a relative path to  {@link #gradleCacheHome} . That way Gradle can recognize equal dependencies.</td>
            </tr>
            <tr>
                <td>
                    <literal>gradleCacheVariable</literal>
                </td>
                <td>File</td>
                <td>null</td>
                <td>The gradle cache dir</td>
                <td>This property is used in conjunction with <code>gradleCacheVariable</code></td>
            </tr>
        </table>
     
        <table id='idea-project'>
            <title>IdeaProject task</title>
            <thead>
                <tr>
                    <td>Property</td>
                    <td>Type</td>
                    <td>Default Value</td>
                    <td>Default Value with Java Plugin</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tr>
                <td>
                    <literal>subprojects</literal>
                </td>
                <td>Set&lt;Project&gt;</td>
                <td><literal>rootProject.allprojects</literal></td>
                <td>-</td>
                <td>The subprojects that should be mapped to modules in the <literal>ipr</literal>
                    file. The subprojects will only be mapped, if the Idea plugin has been
                    applied to them.</td>
            </tr>
            <tr>
                <td>
                    <literal>outputFile</literal>
                </td>
                <td>File</td>
                <td><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable><literal>.ipr</literal></td>
                <td>-</td>
                <td>The ipr file. Used to look for existing files and as the target for generation. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>javaVersion</literal>
                </td>
                <td>String</td>
                <td>1.6</td>
                <td>-</td>
                <td>The java version used for defining the project sdk.</td>
            </tr>
            <tr>
                <td>
                    <literal>wildcards</literal>
                </td>
                <td>Set&lt;String&gt;</td>
                <td>['!?*.java', '!?*.groovy']</td>
                <td>-</td>
                <td>The wildcard resource patterns. Must not be null.</td>
            </tr>
        </table>

        <table id='idea-workspace'>
            <title>IdeaWorkspace task</title>
            <thead>
                <tr>
                    <td>Property</td>
                    <td>Type</td>
                    <td>Default Value</td>
                    <td>Default Value with Java Plugin</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tr>
                <td>
                    <literal>outputFile</literal>
                </td>
                <td>File</td>
                <td><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable><literal>.ipr</literal></td>
                <td>-</td>
                <td>The iws file. Used to look for existing files and as the target for generation. Must not be null.</td>
            </tr>
        </table>


        <table id='idea-task-hooks'>
            <title>Task Hooks</title>
            <thead>
                <tr>
                    <td>Method</td>
                    <td>Project Task Argument</td>
                    <td>Module Task Argument</td>
                    <td>Workspace Task Argument</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tr>
                <td>
                    <literal><code>beforeConfigured { arg -> }</code></literal>
                </td>
                <td><apilink class="org.gradle.plugins.idea.model.Project" lang="groovy"/></td>
                <td><apilink class="org.gradle.plugins.idea.model.Module" lang="groovy"/></td>
                <td>n/a</td>
                <td>Gets called directly after the domain objects have been populated with the content of the
                    existing XML file (if there is one).</td>
            </tr>
            <tr>
                <td>
                    <literal><code>whenConfigured { arg -> }</code></literal>
                </td>
                <td><apilink class="org.gradle.plugins.idea.model.Project" lang="groovy"/></td>
                <td><apilink class="org.gradle.plugins.idea.model.Module" lang="groovy"/></td>
                <td>n/a</td>
                <td>Gets called after the domain objects have been populated with the content of the
                    existing XML file and the content from the build script.</td>
            </tr>
            <tr>
                <td>
                    <literal><code>withXml { arg -> }</code></literal>
                </td>
                <td><code>groovy.util.Node</code></td>
                <td><code>groovy.util.Node</code></td>
                <td><code>groovy.util.Node</code></td>
                <td>The root node of the XML just before the XML is written to disk.</td>
            </tr>
        </table>
    </section>
    <section>
        <title>Customizing the generated files</title>
        <para>Both, the <literal>IdeaProject</literal> and the <literal>IdeaModule</literal> task provide the
            same hooks and behavior for customizing the generated content. The <literal>IdeaWorkspace</literal> only provides a hook
            for direct XML manipulation.</para>
        <para>The plugin recognizes existing idea files. If an idea module, project or workspace file does not exists,
        a default one is used. Otherwise the existing ones are merged.</para>
        <section>
            <title>Merging</title>
            <para>The first option to customize the Idea files is to have an existing Idea <literal>ipr</literal> or
            <literal>iml</literal> file before the tasks are run.
            Existing files will be merged together with the generated content.
            Any section of the existing Idea files that are not the target of generated content will neither be changed nor removed.</para>
            <section id="sec:complete-overwrite">
                <title>Disabling merging with a complete overwrite</title>
                <para>If you want Gradle to completely overwrite existing Idea files you can specify this on the command line by
                    executing something like <userinput>gradle cleanIdea idea</userinput> or <userinput>gradle cleanIdeaModule ideaModule</userinput>.
                    You can specify this also in the build script. Just make the generating tasks depending on the deleting tasks. You can tailor this
                    to your needs. You could make the <literal>idea</literal> task dependent on the <literal>cleanIdea</literal> task. If you only want
                    to overwrite for example the module files you simply make the <literal>ideaModule</literal> task dependent on the
                    <literal>cleanIdeaModule</literal> task.
                </para>
            </section>
        </section>
        <section>
            <title>Hooking into the generation lifecycle</title>
            <para>The Idea plugin provides a couple of domain classes that model Idea project and module files.
                But only the sections that are autogenerated by Gradle. The generation lifecycle is the following.
                If there is an existing Idea file, its whole XML is parsed and stored in memory. Then the domain
                objects are populated with the relevant content of the the existing XML. After that the build script information
                is used to further populate those objects (e.g. add additional dependencies).
                Then all sections modelled by the domain objects are removed from the XML in memory. After that the domain objects are used to inject
                their content into the XML. Finally the XML is written to disk. The lifecycle provides hooks to modify the result according to your needs.
            </para>
            <section id="sec:partial-overwrite">
                <title>Partial Overwrite</title>
                <para>Doing a <link linkend="sec:complete-overwrite">complete overwrite</link> removes all your manual customizations.
                    This might be not what you want.
                    Therefore Gradle provides the option for a partial overwrite. You can hook into the phase just after the existing
                    Idea files have populated the domain objects. You could then for example remove all the dependencies
                    from the <literal>module</literal> object.
                    <sample id="partialOverwrites" dir="idea"
                            title="Partial Overwrite for Module">
                        <sourcefile file="build.gradle" snippet="module-before-configured"/>
                    </sample>
                    The generated XML will have all sections of the existing Idea module file,
                    except for the dependencies, where only the information of the build script is used. You could do something
                    similar for the project file.
                    <sample id="partialOverwritesProject" dir="idea"
                            title="Partial Overwrite for Project">
                        <sourcefile file="build.gradle" snippet="project-before-configured"/>
                    </sample>
                </para>
            </section>
            <section>
                <title>Modifying the fully populated domain objects</title>
                <para>You can also hook into the phase after the existing Idea files and the build script metadata have
                    populated the domain objects. You could then for example export all the dependencies
                    of the <literal>module</literal> object.
                    <sample id="exportDependencies" dir="idea"
                            title="Export Dependencies">
                        <sourcefile file="build.gradle" snippet="module-when-configured"/>
                    </sample>
                    
                </para>
            </section>
            <section>
                <title>Modifying the XML</title>
                <para>You can also hook into the phase after the XML DOM is fully populated but not written to disk.
                    That hook provides total control over what is generated.
                    <sample id="file-dependencies" dir="idea"
                            title="Customizing the XML">
                        <sourcefile file="build.gradle" snippet="project-with-xml"/>
                    </sample>
                </para>
            </section>
        </section>
    </section>
    <section>
        <title>Further things to consider</title>
        <para>The paths of the dependencies in the generated Idea files are absolute. If you manually define a path variable,
            pointing to the gradle dependency cache, Idea will automatically replace the absolute dependency paths with
            this path variable. If you use such a path variable, you need to tell the ideaModule task the name of this variable,
            so that it can do a proper merge without creating duplicates.</para>
    </section>
</chapter>