Merge pull request #43 from apache/SupportExternalAnnotationArtifacts

Support external annotation artifacts
diff --git a/CHANGELOG b/CHANGELOG
index 31c0e6a..f5a8f3e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,7 +3,11 @@
 * Change: Use the `zapwhite` gem to manage file whitespace within repository.
 * Fixed:  Replace references to long removed `http://www.ibiblio.org/maven2/` repository with
           `https://repo1.maven.org/maven2`.
-* Fixed: Ensure pom files used in tests use https when referring to maven repositories.
+* Fixed:  Ensure pom files used in tests use https when referring to maven repositories.
+* Added:  Add support for downloading external annotations and attaching them to IntelliJ IDEA module dependencies.
+* Added:  Detect external annotations in the local project and add them to the generated IntelliJ IDEA
+          module when generating. The default location is `src/main/annotations` but other locations
+          can be specified by modifying the `project.iml.annotation_paths` property.
 
 1.5.7 (2019-02-16)
 * Fixed:  The fix that allowed special characters in usernames and passwords was only partially applied
diff --git a/buildr.gemspec b/buildr.gemspec
index 1b346e8..2866390 100644
--- a/buildr.gemspec
+++ b/buildr.gemspec
@@ -97,5 +97,5 @@
   spec.add_development_dependency 'saikuro_treemap', '0.2.0'
   spec.add_development_dependency 'atoulme-Saikuro', '1.2.1'
   # Used to manage whitespace of files within repository
-  spec.add_development_dependency 'zapwhite', '2.10.0'
+  spec.add_development_dependency 'zapwhite', '2.14.0'
 end
diff --git a/lib/buildr/ide/idea.rb b/lib/buildr/ide/idea.rb
index 02b505d..e1038ba 100644
--- a/lib/buildr/ide/idea.rb
+++ b/lib/buildr/ide/idea.rb
@@ -212,6 +212,10 @@
         'iml'
       end
 
+      def annotation_paths
+        @annotation_paths ||= [buildr_project._(:source, :main, :annotations)].select {|p| File.exist?(p)}
+      end
+
       def main_source_directories
         @main_source_directories ||= [buildr_project.compile.sources].flatten.compact
       end
@@ -342,10 +346,7 @@
         buildr_project.assets.paths.each {|p| default_webroots[p] = '/' }
         webroots = options[:webroots] || default_webroots
         default_deployment_descriptors = []
-        ['web.xml', 'sun-web.xml', 'glassfish-web.xml', 'jetty-web.xml', 'geronimo-web.xml',
-         'context.xml', 'weblogic.xml',
-         'jboss-deployment-structure.xml', 'jboss-web.xml',
-         'ibm-web-bnd.xml', 'ibm-web-ext.xml', 'ibm-web-ext-pme.xml'].
+        %w(web.xml sun-web.xml glassfish-web.xml jetty-web.xml geronimo-web.xml context.xml weblogic.xml jboss-deployment-structure.xml jboss-web.xml ibm-web-bnd.xml ibm-web-ext.xml ibm-web-ext-pme.xml).
           each do |descriptor|
           webroots.each_pair do |path, relative_url|
             next unless relative_url == '/'
@@ -446,9 +447,7 @@
         ejb_roots = options[:ejb_roots] || default_ejb_roots
 
         default_deployment_descriptors = []
-        ['ejb-jar.xml', 'glassfish-ejb-jar.xml', 'ibm-ejb-jar-bnd.xml', 'ibm-ejb-jar-ext-pme.xml', 'ibm-ejb-jar-ext.xml',
-         'jboss.xml', 'jbosscmp-jdbc.xml', 'openejb-jar.xml', 'sun-cmp-mapping.xml', 'sun-ejb-jar.xml',
-         'weblogic-cmp-rdbms-jar.xml', 'weblogic-ejb-jar.xml'].
+        %w(ejb-jar.xml glassfish-ejb-jar.xml ibm-ejb-jar-bnd.xml ibm-ejb-jar-ext-pme.xml ibm-ejb-jar-ext.xml jboss.xml jbosscmp-jdbc.xml openejb-jar.xml sun-cmp-mapping.xml sun-ejb-jar.xml weblogic-cmp-rdbms-jar.xml weblogic-ejb-jar.xml).
           each do |descriptor|
           ejb_roots.each do |path|
             d = "#{path}/WEB-INF/#{descriptor}"
@@ -483,12 +482,18 @@
           dependency_path = d.to_s
           export = true
           source_path = nil
+          annotations_path = nil
           if d.respond_to?(:to_spec_hash)
             source_spec = d.to_spec_hash.merge(:classifier => 'sources')
             source_path = Buildr.artifact(source_spec).to_s
             source_path = nil unless File.exist?(source_path)
           end
-          [dependency_path, export, source_path]
+          if d.respond_to?(:to_spec_hash)
+            annotations_spec = d.to_spec_hash.merge(:classifier => 'annotations')
+            annotations_path = Buildr.artifact(annotations_spec).to_s
+            annotations_path = nil unless File.exist?(annotations_path)
+          end
+          [dependency_path, export, source_path, annotations_path]
         end
       end
 
@@ -499,12 +504,18 @@
           dependency_path = d.to_s
           export = main_dependencies_paths.include?(dependency_path)
           source_path = nil
+          annotations_path = nil
           if d.respond_to?(:to_spec_hash)
             source_spec = d.to_spec_hash.merge(:classifier => 'sources')
             source_path = Buildr.artifact(source_spec).to_s
             source_path = nil unless File.exist?(source_path)
           end
-          [dependency_path, export, source_path]
+          if d.respond_to?(:to_spec_hash)
+            annotations_spec = d.to_spec_hash.merge(:classifier => 'annotations')
+            annotations_path = Buildr.artifact(annotations_spec).to_s
+            annotations_path = nil unless File.exist?(annotations_path)
+          end
+          [dependency_path, export, source_path, annotations_path]
         end
       end
 
@@ -550,22 +561,22 @@
           end
 
           main_project_dependencies = project_dependencies.dup
-          self.test_dependency_details.each do |dependency_path, export, source_path|
+          self.test_dependency_details.each do |dependency_path, export, source_path, annotations_path|
             next if export
-            generate_lib(xml, dependency_path, export, source_path, project_dependencies)
+            generate_lib(xml, dependency_path, export, source_path, annotations_path, project_dependencies)
           end
 
           test_project_dependencies = project_dependencies - main_project_dependencies
-          self.main_dependency_details.each do |dependency_path, export, source_path|
+          self.main_dependency_details.each do |dependency_path, export, source_path, annotations_path|
             next unless export
-            generate_lib(xml, dependency_path, export, source_path, test_project_dependencies)
+            generate_lib(xml, dependency_path, export, source_path, annotations_path, test_project_dependencies)
           end
 
           xml.orderEntryProperties
         end
       end
 
-      def generate_lib(xml, dependency_path, export, source_path, project_dependencies)
+      def generate_lib(xml, dependency_path, export, source_path, annotations_path, project_dependencies)
         project_for_dependency = Buildr.projects.detect do |project|
           [project.packages, project.compile.target, project.resources.target, project.test.compile.target, project.test.resources.target].flatten.
             detect { |artifact| artifact.to_s == dependency_path }
@@ -578,7 +589,7 @@
           end
           project_dependencies << project_for_dependency
         else
-          generate_module_lib(xml, url_for_path(dependency_path), export, (source_path ? url_for_path(source_path) : nil), !export)
+          generate_module_lib(xml, url_for_path(dependency_path), export, (source_path ? url_for_path(source_path) : nil), (annotations_path ? url_for_path(annotations_path) : nil), !export)
         end
       end
 
@@ -602,6 +613,14 @@
         xml.output(:url => file_path(self.main_output_dir.to_s))
         xml.tag!('output-test', :url => file_path(self.test_output_dir.to_s))
         xml.tag!('exclude-output')
+        paths = self.annotation_paths
+        unless paths.empty?
+          xml.tag!('annotation-paths') do |xml|
+            paths.each do |path|
+              xml.root(:url=> file_path(path))
+            end
+          end
+        end
       end
 
       def generate_content(xml)
@@ -654,12 +673,15 @@
         xml.orderEntry attribs
       end
 
-      def generate_module_lib(xml, path, export, source_path, test = false)
+      def generate_module_lib(xml, path, export, source_path, annotations_path, test = false)
         attribs = {:type => 'module-library'}
         attribs[:exported] = '' if export
         attribs[:scope] = 'TEST' if test
         xml.orderEntry attribs do
           xml.library do
+            xml.ANNOTATIONS do
+              xml.root :url => annotations_path
+            end if annotations_path
             xml.CLASSES do
               xml.root :url => path
             end
diff --git a/lib/buildr/packaging/artifact.rb b/lib/buildr/packaging/artifact.rb
index 2af68c7..27f87be 100644
--- a/lib/buildr/packaging/artifact.rb
+++ b/lib/buildr/packaging/artifact.rb
@@ -21,6 +21,9 @@
   desc "Download all artifacts' sources"
   task 'artifacts:sources'
 
+  desc "Download all artifacts' external annotations"
+  task 'artifacts:annotations'
+
   desc "Download all artifacts' javadoc"
   task 'artifacts:javadoc'
 
@@ -127,7 +130,7 @@
     def sources_artifact
       sources_spec = to_spec_hash.merge(:classifier=>'sources')
       sources_task = OptionalArtifact.define_task(Buildr.repositories.locate(sources_spec))
-      sources_task.send :apply_spec, sources_spec
+      sources_task.send :apply_spec, sources_spec if sources_task.respond_to?(:apply_spec)
       sources_task
     end
 
@@ -138,11 +141,23 @@
     def javadoc_artifact
       javadoc_spec = to_spec_hash.merge(:classifier=>'javadoc')
       javadoc_task = OptionalArtifact.define_task(Buildr.repositories.locate(javadoc_spec))
-      javadoc_task.send :apply_spec, javadoc_spec
+      javadoc_task.send :apply_spec, javadoc_spec if javadoc_task.respond_to?(:apply_spec)
       javadoc_task
     end
 
     # :call-seq:
+    #   annotations_artifact => Artifact
+    #
+    # Convenience method that returns an annotations artifact. The annotations artifact is used by
+    # Intellij IDEA as a source of external annotations.
+    def annotations_artifact
+      annotations_spec = to_spec_hash.merge(:classifier=>'annotations')
+      annotations_task = OptionalArtifact.define_task(Buildr.repositories.locate(annotations_spec))
+      annotations_task.send :apply_spec, annotations_spec if annotations_task.respond_to?(:apply_spec)
+      annotations_task
+    end
+
+    # :call-seq:
     #   pom_xml => string
     #
     # Creates POM XML for this artifact.
@@ -948,6 +963,7 @@
       unless spec[:type] == :pom
         Rake::Task['artifacts:sources'].enhance [task.sources_artifact]
         Rake::Task['artifacts:javadoc'].enhance [task.javadoc_artifact]
+        Rake::Task['artifacts:annotations'].enhance [task.annotations_artifact]
       end
     end
     task.enhance &block