<!--
  ~ 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</ulink>, thus
        making it possible to open the project from IDEA (<guimenuitem>File</guimenuitem> - <guimenuitem>Open Project</guimenuitem>).
        Both external dependencies (including associated source and javadoc files) and project dependencies are considered.</para>

    <para>What exactly the IDEA plugin generates depends on which other plugins are used:</para>
    <table>
        <title>IDEA plugin behavior</title>
        <thead>
            <tr>
                <td>Plugin</td><td>Description</td>
            </tr>
        </thead>
        <tr>
            <td>None</td><td>Generates an IDEA module file. Also generates an IDEA project and workspace file if the project
            is the root project.</td>
        </tr>
        <tr>
            <td>
                <link linkend="java_plugin">Java</link>
            </td>
            <td>Adds Java configuration to the module and project files.</td>
        </tr>
    </table>

    <para>One focus of the IDEA plugin is to be open to customization. Each task provides a standardized set of hooks
        for adding and removing content from the generated files.
    </para>

    <section>
        <title>Usage</title>
        <para>To use the IDEA plugin, include this in your build script:</para>
        <sample id="useIdeaPlugin" dir="idea" title="Using the IDEA plugin">
            <sourcefile file="build.gradle" snippet="use-plugin"/>
        </sample>
        <para>The IDEA plugin adds a number of tasks to your project. The main tasks that you will use
            are the <literal>idea</literal> and <literal>cleanIdea</literal> tasks.
        </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 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.ide.idea.GenerateIdeaProject"/></td>
                <td>Generates the <literal>.ipr</literal> file. This task is only added to the root project.</td>
            </tr>
            <tr>
                <td>
                    <literal>ideaModule</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaModule"/></td>
                <td>Generates the <literal>.iml</literal> file</td>
            </tr>
            <tr>
                <td>
                    <literal>ideaWorkspace</literal>
                </td>
                <td>
                    <literal>-</literal>
                </td>
                <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaWorkspace"/></td>
                <td>Generates the <literal>.iws</literal> file. This task is only added to the root project.</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>
                    <classname>File</classname>
                </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>
                    <classname>File</classname>
                </td>
                <td><literal><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable>.iml</literal></td>
                <td>-</td>
                <td>The <filename>.iml</filename> file. Used to look for existing files and as the target for generation. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>inheritOutputDirs</literal>
                </td>
                <td>
                    <classname>Boolean</classname>
                </td>
                <td>null</td>
                <td>null</td>
                <td>If <literal>null</literal>, the value in the existing or default module file is used (the default file uses
                    <literal>true</literal>). If <literal>true</literal>, the module output directories will be located below the
                    project output directories. If <literal>false</literal>, the directories specified with the <literal>sourceDirs</literal>
                    and <literal>testSourceDirs</literal> properties are used.</td>
            </tr>
            <tr>
                <td>
                    <literal>sourceDirs</literal>
                </td>
                <td>
                    <classname>Set&lt;File&gt;</classname>
                </td>
                <td>empty set</td>
                <td>The source directories of <literal>sourceSets.main</literal></td>
                <td>The directories containing the production sources. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>testSourceDirs</literal>
                </td>
                <td>
                    <classname>Set&lt;File&gt;</classname>
                </td>
                <td>empty set</td>
                <td>The source directories of <literal>sourceSets.test</literal></td>
                <td>The directories containing the test sources. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>excludeDirs</literal>
                </td>
                <td>
                    <classname>Set&lt;File&gt;</classname>
                </td>
                <td>empty set</td>
                <td>-</td>
                <td>The directories to be excluded by IDEA. Must not be null.</td>
            </tr>
            <tr>
                <td>
                    <literal>outputDir</literal>
                </td>
                <td>
                    <classname>File</classname>
                </td>
                <td>
                    <literal>null</literal>
                </td>
                <td><literal>null</literal></td>
                <td>The IDEA output directory for the production sources. If null, no entry is created.</td>
            </tr>
            <tr>
                <td>
                    <literal>testOutputDir</literal>
                </td>
                <td>
                    <classname>File</classname>
                </td>
                <td>
                    <literal>null</literal>
                </td>
                <td><literal>null</literal></td>
                <td>The IDEA output directory for the test sources. If null, no entry is created.</td>
            </tr>
            <tr>
                <td>
                    <literal>javaVersion</literal>
                </td>
                <td>
                    <classname>String</classname>
                </td>
                <td>
                    <literal>null</literal>
                </td>
                <td>-</td>
                <td>If null, the value of the existing or default <literal>.ipr</literal> file is used (the default file uses
                    <literal>inherited</literal>). If set to <literal>inherited</literal>, the project SDK is used. Otherwise,
                    the SDK for the corresponding value of java version is used.</td>
            </tr>
            <tr>
                <td>
                    <literal>downloadSources</literal>
                </td>
                <td>
                    <classname>boolean</classname>
                </td>
                <td>
                    <literal>true</literal>
                </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>
                    <classname>boolean</classname>
                </td>
                <td>
                    <literal>false</literal>
                </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>
                    <classname>Map</classname>
                </td>
                <td>
                    <classname>[:]</classname>
                </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>variables</literal>
                </td>
                <td>
                    <classname>Map&lt;String, File></classname>
                </td>
                <td>
                    <literal>[:]</literal>
                </td>
                <td>-</td>
                <td>The variables to be used for replacing absolute paths in the .iml file. For example, you might add
                    a <literal>GRADLE_USER_HOME</literal> variable to point to the Gradle user home directory.</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>
                    <classname>Set&lt;Project&gt;</classname>
                </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>
                    <classname>File</classname>
                </td>
                <td><literal><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable>.ipr</literal></td>
                <td>-</td>
                <td>The <literal>.ipr</literal> 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>
                    <classname>String</classname>
                </td>
                <td>
                    <literal>1.6</literal>
                </td>
                <td>-</td>
                <td>The Java version used for defining the project SDK.</td>
            </tr>
            <tr>
                <td>
                    <literal>wildcards</literal>
                </td>
                <td>
                    <classname>Set&lt;String&gt;</classname>
                </td>
                <td>
                    <literal>['!?*.java', '!?*.groovy']</literal>
                </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>
                    <classname>File</classname>
                </td>
                <td><literal><replaceable>projectDir</replaceable>/<replaceable>&lt;project.name&gt;</replaceable>.iws</literal></td>
                <td>-</td>
                <td>The <literal>.iws</literal> file. Used to look for existing files and as the target for generation. Must not be null.</td>
            </tr>
        </table>
    </section>
    <section>
        <title>Customizing the generated files</title>
        <para>All IDEA tasks provide the same hooks and behavior for customizing the generated content.
            However, the workspace file can effectively only be manipulated via the <code>withXml</code> hook,
            because its corresponding domain object is essentially empty.</para>
        <para>The tasks recognize existing IDEA files, and merge them with the generated content.</para>
        <section>
            <title>Merging</title>
            <para>Sections of existing IDEA files that are also the target of generated content will be amended or overwritten,
                depending on the particular section. The remaining sections will be left as-is.</para>
            <section id="sec:complete-overwrite">
                <title>Disabling merging with a complete overwrite</title>
                <para>To completely overwrite existing IDEA files, execute a clean task together with its corresponding generation task,
                    for example <userinput>gradle cleanIdea idea</userinput> (in that order). If you want to make this
                    the default behavior, add <code>idea.dependsOn(cleanIdea)</code> to your build script. This makes it
                    unnecessary to execute the clean task explicitly.
                </para>
                <para>Complete overwrite works equally well for individual files, for example by executing<userinput>gradle cleanIdeaModule ideaModule</userinput>.</para>
            </section>
        </section>
        <section>
            <title>Hooking into the generation lifecycle</title>
            <para>The IDEA plugin provides domain classes modeling the sections of the IDEA files
                that are autogenerated by Gradle. The generation lifecycle is as follows:
                <orderedlist>
                    <listitem>If there is an existing file, its whole XML content is parsed and stored in memory; otherwise, a default file is used in its place</listitem>
                    <listitem>The domain objects are populated with the relevant content of the existing file</listitem>
                    <listitem>The <code>beforeConfigured</code> hook is executed</listitem>
                    <listitem>The domain objects are populated with content from Gradle's build model, which may require merging with content from the existing file</listitem>
                    <listitem>The <code>whenConfigured</code> hook is executed</listitem>
                    <listitem>All sections modeled by the domain objects are removed from the in-memory XML representation</listitem>
                    <listitem>The domain objects inject their content into the in-memory XML representation</listitem>
                    <listitem>The <code>withXml</code> hook is executed</listitem>
                    <listitem>The in-memory XML representation is written to disk</listitem>
                </orderedlist>
                The following table lists the domain objects used for each of the IDEA task types:
            </para>
            <table id='idea-task-hooks'>
                <title>Task Hooks</title>
                <thead>
                    <tr>
                        <td>Task type</td>
                        <td><literal>beforeConfigured { arg -> }</literal> argument type</td>
                        <td><literal>whenConfigured { arg -> }</literal> argument type</td>
                        <td><literal>withXml { arg -> }</literal> argument type</td>
                    </tr>
                </thead>
                <tr>
                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaProject"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Project"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Project"/></td>
                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
                </tr>
                <tr>
                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaModule"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Module"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Module"/></td>
                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
                </tr>
                <tr>
                    <td><apilink class="org.gradle.plugins.ide.idea.GenerateIdeaWorkspace"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Workspace"/></td>
                    <td><apilink class="org.gradle.plugins.ide.idea.model.Workspace"/></td>
                    <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
                </tr>
            </table>
            <section id="sec:partial-overwrite">
                <title>Partial overwrite of existing content</title>
                <para>A <link linkend="sec:complete-overwrite">complete overwrite</link> causes all existing content to be discarded,
                    thereby losing any changes made directly in the IDE. The <code>beforeConfigured</code> hook makes it possible
                    to overwrite just certain parts of the existing content. The following example removes all existing dependencies
                    from the <literal>Module</literal> domain object:
                    <sample id="partialOverwrites" dir="idea"
                            title="Partial Overwrite for Module">
                        <sourcefile file="build.gradle" snippet="module-before-configured"/>
                    </sample>
                    The resulting module file will only contain Gradle-generated dependency entries, but
                    not any other dependency entries that may have been present in the original file. (In the case of dependency entries,
                    this is also the default behavior.) Other sections of the module file will be either left as-is or merged.
                    The same could be done for the module paths in 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>The <code>whenConfigured</code> hook allows to manipulate the fully populated domain objects. Often this is the
                    preferred way to customize IDEA files. Here is how you would export all the dependencies of an IDEA module:
                    <sample id="exportDependencies" dir="idea"
                            title="Export Dependencies">
                        <sourcefile file="build.gradle" snippet="module-when-configured"/>
                    </sample>
                    
                </para>
            </section>
            <section>
                <title>Modifying the XML representation</title>
                <para>The <code>withXml</code>hook allows to manipulate the in-memory XML representation just before the file gets written to disk.
                    Although Groovy's XML support makes up for a lot, this approach is less convenient than manipulating the domain objects.
                    In return, you get total control over the generated file, including sections not modeled by the domain objects.
                    <sample id="projectWithXml" dir="idea"
                            title="Customizing the XML">
                        <sourcefile file="build.gradle" snippet="project-with-xml"/>
                        <test args="idea"/>
                    </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>