You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@creadur.apache.org by po...@apache.org on 2014/08/06 23:16:31 UTC

svn commit: r1616351 - in /creadur/whisker/trunk: ./ apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/ apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ apache-whisker-app/src/main/java/org/apache/creadur/whisker/...

Author: pottlinger
Date: Wed Aug  6 21:16:30 2014
New Revision: 1616351

URL: http://svn.apache.org/r1616351
Log:
Reducing warnings and updating dependencies

-Updated plugins and dependencies (including SCM/SSH) in whisker's parent pom after upgrading to new parent pom apache:14
-Fixing javadoc that prevented a build with JDK8.
-Using annotation-based maven plugin configuration, updated maven dependencies for that.
-LHF: Removed SuppressWarnings by removing empty if statements.


Modified:
    creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/Whisker.java
    creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/LicenseAnalyst.java
    creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ResourceSourceAuditor.java
    creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/load/StreamableResourceFactory.java
    creadur/whisker/trunk/apache-whisker-cli/src/main/java/org/apache/creadur/whisker/cli/Main.java
    creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/NoticeCollator.java
    creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/Resource.java
    creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/ResourceNamesCollator.java
    creadur/whisker/trunk/apache-whisker-plugin4maven/pom.xml
    creadur/whisker/trunk/apache-whisker-plugin4maven/src/main/java/org/apache/creadur/whisker/plugin/maven/GenerateMojo.java
    creadur/whisker/trunk/apache-whisker-velocity/src/main/java/org/apache/creadur/whisker/out/velocity/RenderingHelper.java
    creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/JDomBuilder.java
    creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/MissingIDException.java
    creadur/whisker/trunk/pom.xml

Modified: creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/Whisker.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/Whisker.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/Whisker.java (original)
+++ creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/Whisker.java Wed Aug  6 21:16:30 2014
@@ -69,7 +69,7 @@ public class Whisker {
     }
 
     /**
-     * Gets the factory that builds product {@link Writer}s.
+     * Gets the factory that builds product {@link java.io.Writer}s.
      * @return factory
      */
     public final ResultWriterFactory getWriterFactory() {
@@ -77,7 +77,7 @@ public class Whisker {
     }
 
     /**
-     * Sets the factory that builds product {@link Writer}s.
+     * Sets the factory that builds product {@link java.io.Writer}s.
      * @param writerFactory not null
      * @return this, not null
      */

Modified: creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/LicenseAnalyst.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/LicenseAnalyst.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/LicenseAnalyst.java (original)
+++ creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/LicenseAnalyst.java Wed Aug  6 21:16:30 2014
@@ -209,14 +209,11 @@ public class LicenseAnalyst {
      * @param directory not null
      * @param collator not null
      */
-    @SuppressWarnings("PMD.EmptyIfStmt")
     private void analyseExtraLicenses(final Directory directory,
             final ResourceNamesCollator collator) {
         final Collection<String> actualResources = directory.getContents();
         for (final String resourceLicense: collator.getNames()) {
-            if (actualResources.contains(resourceLicense)) {
-                // Fine
-            } else {
+            if (!actualResources.contains(resourceLicense)) {
                 getExtraLicenses().add(
                         new ResourceDescription(
                                 directory.getName(),
@@ -230,14 +227,11 @@ public class LicenseAnalyst {
      * @param directory not null
      * @param collator not null
      */
-    @SuppressWarnings("PMD.EmptyIfStmt")
     private void analyseMissingLicenses(final Directory directory,
             final ResourceNamesCollator collator) {
         final Collection<String> licensedResources = collator.getNames();
         for (final String actualResource: directory.getContents()) {
-            if (licensedResources.contains(actualResource)) {
-                // Fine
-            } else {
+            if (!licensedResources.contains(actualResource)) {
                 getMissingLicenses().add(
                         new ResourceDescription(
                                 directory.getName(),

Modified: creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ResourceSourceAuditor.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ResourceSourceAuditor.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ResourceSourceAuditor.java (original)
+++ creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/analysis/ResourceSourceAuditor.java Wed Aug  6 21:16:30 2014
@@ -104,7 +104,6 @@ public final class ResourceSourceAuditor
      * @see Visitor#visit(Resource)
      */
     @Override
-    @SuppressWarnings("PMD.EmptyIfStmt")
     public void visit(final Resource resource) {
         if (lastLicense == null) {
             throw new IllegalArgumentException(
@@ -120,8 +119,6 @@ public final class ResourceSourceAuditor
             } else {
                 resourcesMissingSource.add(pair);
             }
-        } else {
-            // Source not required
         }
     }
 

Modified: creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/load/StreamableResourceFactory.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/load/StreamableResourceFactory.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/load/StreamableResourceFactory.java (original)
+++ creadur/whisker/trunk/apache-whisker-app/src/main/java/org/apache/creadur/whisker/app/load/StreamableResourceFactory.java Wed Aug  6 21:16:30 2014
@@ -65,7 +65,7 @@ public final class StreamableResourceFac
      * builds an instance that streams, on demand,
      * from the classpath. Otherwise, builds an
      * instances that streams from the file system.
-     * @param resourceName
+     * @param resourceName source stream name.
      * @return not null
      */
     public StreamableResource streamFromResource(final String resourceName) {

Modified: creadur/whisker/trunk/apache-whisker-cli/src/main/java/org/apache/creadur/whisker/cli/Main.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-cli/src/main/java/org/apache/creadur/whisker/cli/Main.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-cli/src/main/java/org/apache/creadur/whisker/cli/Main.java (original)
+++ creadur/whisker/trunk/apache-whisker-cli/src/main/java/org/apache/creadur/whisker/cli/Main.java Wed Aug  6 21:16:30 2014
@@ -70,7 +70,7 @@ public final class Main {
     /**
      * Bootstraps application.
      * @param args not null
-     * @throws Exception when application unexpectedly fails
+     * @throws Exception when application fails unexpectedly
      */
     public static void main(final String[] args) throws Exception {
         System.exit(new Main(app()).run(args));
@@ -196,7 +196,7 @@ public final class Main {
      * @param args not null
      * @return true when command line contains option for help,
      * false otherwise
-     * @throws ParseException
+     * @throws ParseException in case options could not be read properly.
      */
     public boolean printHelp(String[] args) throws ParseException {
         final CommandLineOption help = CommandLineOption.PRINT_HELP;

Modified: creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/NoticeCollator.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/NoticeCollator.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/NoticeCollator.java (original)
+++ creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/NoticeCollator.java Wed Aug  6 21:16:30 2014
@@ -70,8 +70,8 @@ public class NoticeCollator extends Visi
     }
 
     /**
-     * @param notices
-     * @return
+     * @param notices map of notice-id and notices.
+     * @return list of notices.
      */
     public Set<String> notices(final Map<String, String> notices) {
         final Set<String> results = new HashSet<String>();

Modified: creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/Resource.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/Resource.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/Resource.java (original)
+++ creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/Resource.java Wed Aug  6 21:16:30 2014
@@ -120,10 +120,10 @@ public class Resource implements Compara
     }
 
     /**
-     * Based on name.
+     * Comparison happens based on name.
      * @see java.lang.Comparable#compareTo(java.lang.Object)
-     * @param other
-     * @return comparison based on name
+     * @param other resource to compare to.
+     * @return result of comparison based on name
      */
     public int compareTo(final Resource other) {
         return getName().compareTo(other.getName());

Modified: creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/ResourceNamesCollator.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/ResourceNamesCollator.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/ResourceNamesCollator.java (original)
+++ creadur/whisker/trunk/apache-whisker-model/src/main/java/org/apache/creadur/whisker/model/ResourceNamesCollator.java Wed Aug  6 21:16:30 2014
@@ -75,12 +75,9 @@ public class ResourceNamesCollator exten
      * @param resource not null
      */
     @Override
-    @SuppressWarnings("PMD.EmptyIfStmt")
     public void visit(final Resource resource) {
-        if (this.resources.add(new ImmutablePair<WithinDirectory, Resource>(
+        if (!this.resources.add(new ImmutablePair<WithinDirectory, Resource>(
                 this.lastDirectory, resource))) {
-            // Fine
-        } else {
             // Already added
             if (this.lastDirectory == null) {
                 // Issue with logic which will result in a null pointer later
@@ -89,6 +86,6 @@ public class ResourceNamesCollator exten
             }
             this.duplicates.add(new ImmutablePair<WithinDirectory, Resource>(
                     this.lastDirectory, resource));
-        }
+      }
     }
 }

Modified: creadur/whisker/trunk/apache-whisker-plugin4maven/pom.xml
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-plugin4maven/pom.xml?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-plugin4maven/pom.xml (original)
+++ creadur/whisker/trunk/apache-whisker-plugin4maven/pom.xml Wed Aug  6 21:16:30 2014
@@ -33,9 +33,14 @@
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
-      <version>2.0</version>
+      <version>3.2.2</version>
+    </dependency>
+    <dependency>
+  	  <groupId>org.apache.maven.plugin-tools</groupId>
+	  <artifactId>maven-plugin-annotations</artifactId>
+	  <!-- keep in sync with maven-plugin-plugin version derived from parent -->
+	  <version>3.2</version>
     </dependency>
-
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
@@ -87,10 +92,8 @@
       <groupId>commons-collections</groupId>
       <artifactId>commons-collections</artifactId>
     </dependency>
-
   </dependencies>
 
-
   <build>
     <plugins>
       <plugin>
@@ -136,17 +139,17 @@
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-project-info-reports-plugin</artifactId>
-                <version>2.4</version>
+                <version>2.7</version>
               </plugin>
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-report-plugin</artifactId>
-                <version>2.12</version>
+                <version>2.17</version>
               </plugin>
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jxr-plugin</artifactId>
-                <version>2.3</version>
+                <version>2.4</version>
                 <configuration>
                   <linkJavadoc>true</linkJavadoc>
                 </configuration>
@@ -154,12 +157,12 @@
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-plugin-plugin</artifactId>
-                <version>3.0</version>
+                <version>3.2</version>
               </plugin>
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.8.1</version>
+                <version>2.9.1</version>
               </plugin>
             </reportPlugins>
           </configuration>

Modified: creadur/whisker/trunk/apache-whisker-plugin4maven/src/main/java/org/apache/creadur/whisker/plugin/maven/GenerateMojo.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-plugin4maven/src/main/java/org/apache/creadur/whisker/plugin/maven/GenerateMojo.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-plugin4maven/src/main/java/org/apache/creadur/whisker/plugin/maven/GenerateMojo.java (original)
+++ creadur/whisker/trunk/apache-whisker-plugin4maven/src/main/java/org/apache/creadur/whisker/plugin/maven/GenerateMojo.java Wed Aug  6 21:16:30 2014
@@ -20,42 +20,39 @@ package org.apache.creadur.whisker.plugi
 
 import java.io.File;
 
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.creadur.whisker.app.Act;
 import org.apache.creadur.whisker.app.Whisker;
 import org.apache.creadur.whisker.app.load.StreamableResourceFactory;
 import org.apache.creadur.whisker.app.out.WriteResultsIntoDirectoryFactory;
 import org.apache.creadur.whisker.out.velocity.VelocityEngine;
-
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.*;
 
 /**
  * Generates licensing related materials such as LICENSE and NOTICE documents
- * for assembled applications.
- * @goal generate
+ * for assembled applications. The plugin is not bound to a specific lifecycle phase.
  */
+@Mojo(name = "generate")
 public class GenerateMojo extends AbstractMojo {
 
     /**
      * Destination for generated materials
-     *
-     * @parameter default-value="${project.build.directory}"
      */
+	@Parameter(defaultValue = "${project.build.directory}")
     private File outputDirectory;
 
     /**
      * The licensing materials will be encoding thus.
-     * @parameter property="outputEncoding" default-value="${project.build.sourceEncoding}"
      */
+	@Parameter(property = "outputEncoding", defaultValue = "${project.build.sourceEncoding}")
     private String outputEncoding;
 
     /**
      * This file contains a description of the licensing qualities of
      * the expected contents of the assembled application.
-     *
-     * @required
-     * @parameter property="apacheWhiskerDescriptor"
      */
+	@Parameter(property = "apacheWhiskerDescriptor", required = true)
     private File descriptor;
 
     /**

Modified: creadur/whisker/trunk/apache-whisker-velocity/src/main/java/org/apache/creadur/whisker/out/velocity/RenderingHelper.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-velocity/src/main/java/org/apache/creadur/whisker/out/velocity/RenderingHelper.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-velocity/src/main/java/org/apache/creadur/whisker/out/velocity/RenderingHelper.java (original)
+++ creadur/whisker/trunk/apache-whisker-velocity/src/main/java/org/apache/creadur/whisker/out/velocity/RenderingHelper.java Wed Aug  6 21:16:30 2014
@@ -41,8 +41,9 @@ public class RenderingHelper {
     private final Configuration configuration;
 
     /**
-     * Constructs a helper for the given work.
+     * Constructs a helper for the given work and configuration.
      * @param work not null
+     * @param configuration not null
      */
     public RenderingHelper(final Descriptor work, final Configuration configuration) {
         super();

Modified: creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/JDomBuilder.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/JDomBuilder.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/JDomBuilder.java (original)
+++ creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/JDomBuilder.java Wed Aug  6 21:16:30 2014
@@ -48,457 +48,575 @@ import org.jdom.input.SAXBuilder;
  * Builds a model from xml using JDOM.
  */
 public class JDomBuilder {
-    
-    private static final String COPYRIGHT_NOTICE_NAME = "copyright-notice";
-    /**
+
+	private static final String COPYRIGHT_NOTICE_NAME = "copyright-notice";
+	/**
      * 
      */
-    private static final String LICENSE_ELEMENT_NAME = "license";
-    /**
+	private static final String LICENSE_ELEMENT_NAME = "license";
+	/**
      * 
      */
-    private static final String PRIMARY_LICENSE_NAME = "primary-license";
-    /** Names the element representing an organisation */
-    private static final String ORGANISATION_ELEMENT_NAME = "organisation";
-    /** Names the element representing a resource */
-    private static final String RESOURCE_ELEMENT_NAME = "resource";
-
-    /**
-     * Builds a resource.
-     * @param element not null
-     * @return built resource, not null
-     * @throws UnexpectedElementException when element is not named 'resource'
-     */
-    public Resource resource(Element element) throws UnexpectedElementException {
-        if (RESOURCE_ELEMENT_NAME.equals(element.getName())) {
-            return new Resource(StringUtils.trim(element.getAttributeValue("name")), 
-                    StringUtils.trim(element.getAttributeValue("notice")),
-                    StringUtils.trim(element.getAttributeValue("source")));
-        } else {
-            throw unexpectedElementException(element, RESOURCE_ELEMENT_NAME);
-        }
-    }
-
-    /**
-     * Builds a suitable exception when the element name is unexpected.
-     * @param element, not null
-     * @param expectedElement, not null
-     * @return a suitable exception, not null
-     */
-    private UnexpectedElementException unexpectedElementException(Element element,
-            final String expectedElement) {
-        return new UnexpectedElementException(expectedElement, element.getName());
-    }
-
-    /**
-     * Builds an organisation model from xml.
-     * @param element, not null
-     * @return {@link Organisation} not null
-     * @throws UnexpectedElementException when element is not named 'organisation'
-     */
-    public Organisation organisation(Element element) throws UnexpectedElementException {
-        if (ORGANISATION_ELEMENT_NAME.equals(element.getName())) {
-            return new Organisation(
-                    element.getAttributeValue("id"), 
-                    element.getAttributeValue("name"), 
-                    element.getAttributeValue("url"));
-        } else {
-            throw unexpectedElementException(element, ORGANISATION_ELEMENT_NAME);
-        }
-    }
-
-    /**
-     * @param element
-     * @return
-     */
-    @SuppressWarnings("unchecked")
-    public Collection<Resource> collectResources(Element element) {
-        final Collection<Resource> resources = new TreeSet<Resource>();
-        for (Element resourceElement: (List<Element>) element.getChildren("resource")) {
-            resources.add(new JDomBuilder().resource(resourceElement));
-        }
-        return Collections.unmodifiableCollection(resources);
-    }
-
-    /**
-     * Finds the organisation linked by ID from the given element.
-     * @param element modelled ByOrganisation, not null
-     * @param organisationsById organisations identified, not null
-     * @throws MissingIDException when the linked organisation is not found in the given map
-     */
-    public Organisation organisation(final Element element,
-            final Map<String, Organisation> organisationsById) throws MissingIDException {
-        final String id = element.getAttributeValue("id");
-        if (organisationsById.containsKey(id)) {
-            return organisationsById.get(id);
-        } else {
-            throw new MissingIDException(ORGANISATION_ELEMENT_NAME, element.getName(), id);
-        }
-    }
-    
-    /**
-     * Builds a by-organisation model from xml.
-     * @param element not null
-     * @param organisation not null
-     * @return not null
-     */
-    public ByOrganisation byOrganisation(final Element element, final Organisation organisation) {
-        return new ByOrganisation(organisation, collectResources(element));
-    }
-
-    /**
-     * Builds a by-organisation model from xml.
-     * @param byOrganisation not null
-     * @param organisationsById not null 
-     * @return not null
-     * @throws MissingIDException when the linked organisation is not found in the given map
-     */
-    public ByOrganisation byOrganisation(final Element byOrganisation, 
-            final Map<String, Organisation> organisationsById) throws MissingIDException  {
-        return byOrganisation(byOrganisation, organisation(byOrganisation, organisationsById));
-    }
-
-    /**
-     * Collects by-organisation children.
-     * @param parent not null
-     * @param map not null
-     * @return unmodifiable set sort by natural order, not null
-     */
-    @SuppressWarnings("unchecked")
-    public SortedSet<ByOrganisation> collectByOrganisations(final Element parent, 
-            final Map<String, Organisation> map) {
-        final SortedSet<ByOrganisation> results = new TreeSet<ByOrganisation>();
-        if (parent != null) {
-            for (final Element byOrgElement: (List<Element>) parent.getChildren("by-organisation")) {
-                results.add(byOrganisation(byOrgElement, map));
-            }
-        }
-        return Collections.unmodifiableSortedSet(results);
-    }
-
-    /**
-     * Builds a license model from xml.
-     * @param element not null
-     * @return not null
-     */
-    public License license(Element element) {
-        final Element text = element.getChild("text");
-        return new License("yes".equalsIgnoreCase(element.getAttributeValue("requires-source")), 
-                text == null ? "" : text.getText(), 
-                expectedParameters(element), 
-                element.getAttributeValue("id"), 
-                element.getAttributeValue("url"),
-                element.getAttributeValue("name"));
-    }
-    
-    @SuppressWarnings("unchecked")
-    private Collection<String> expectedParameters(final Element element) {
-        final Collection<String> results = new HashSet<String>();
-        final Element templateElement = element.getChild("template");
-        if (templateElement != null) {
-            for (Element parameterNameElement: (List<Element>) templateElement.getChildren("parameter-name")) {
-                results.add(parameterNameElement.getTextTrim());
-            }
-        }
-        return results;
-    }
-
-    /**
-     * Finds the license with an id matching that referenced by the element.
-     * @param element not null
-     * @param licenses not null
-     * @return not null
-     * @throws MissingIDException when referenced license isn't found in the collection
-     */
-    public License license(final Element element, final Map<String, License> licenses) throws MissingIDException {
-        final String id = element.getAttributeValue("id");
-        if (licenses.containsKey(id)) {
-            return licenses.get(id);
-        } else {
-            throw new MissingIDException(LICENSE_ELEMENT_NAME, element.getName(), id);
-        }
-    }
-
-    /**
-     * Builds a with-license model from xml.
-     * @param element not null
-     * @param licenses not null
-     * @param organisations not null
-     * @return
-     * @throws MissingIDException when referenced license isn't found in the collection
-     */
-    public WithLicense withLicense(Element element,
-            Map<String, License> licenses,
-            Map<String, Organisation> organisations) throws MissingIDException  {
-        return new WithLicense(license(element, licenses), copyrightNotice(element), 
-                parameters(element), collectByOrganisations(element, organisations));
-    }
-    
-    /**
-     * Extracts copyright notice content from with-license.
-     * @param element not null
-     * @return not null
-     */
-    private String copyrightNotice(final Element element) {
-        final String result;
-        final Element copyrightNoticeElement = element.getChild(COPYRIGHT_NOTICE_NAME);
-        if (copyrightNoticeElement == null) {
-            result = null;
-        } else {
-            result = copyrightNoticeElement.getTextTrim();
-        }
-        return result;
-    }
-
-    /**
-     * Builds a list of parameter values by name.
-     * @param element not null
-     * @return parameter values indexed by value, not null
-     * @throws DuplicateElementException when two parameters shared the same name
-     */
-    @SuppressWarnings("unchecked")
-    public Map<String, String> parameters(Element element) throws DuplicateElementException {
-        final Map<String, String> results = new HashMap<String, String>();
-        final Element licenseParametersElement = element.getChild("license-parameters");
-        if (licenseParametersElement != null) {
-            for (Element parameterElement: (List<Element>) licenseParametersElement.getChildren("parameter")) {
-                final String name = parameterElement.getChild("name").getTextTrim();
-                if (results.containsKey(name)) {
-                    throw new DuplicateElementException("Duplicate parameter '" + name + "'");
-                }
-                results.put(name, 
-                        parameterElement.getChild("value").getTextTrim());
-            }   
-        }
-        return results;
-    }
-
-    /**
-     * Collects child with-licenses.
-     * @param licenses not null
-     * @param organisations not null
-     * @param parent not null
-     * @return not null, possibly empty
-     */
-    @SuppressWarnings("unchecked")
-    public Collection<WithLicense> withLicenses(Map<String, License> licenses,
-            Map<String, Organisation> organisations, Element parent) {
-        final List<WithLicense> results = new ArrayList<WithLicense>();
-        for (Element withLicenseElement: (List<Element>) parent.getChildren("with-license")) {
-            results.add(new JDomBuilder().withLicense(withLicenseElement, licenses, organisations));
-        }
-        Collections.sort(results);
-        return results;
-    }
-
-    /**
-     * Collects child organisations of public domain.
-     * @param organisations not null
-     * @param parent not null
-     * @return not null, possibly null
-     */
-    public Collection<ByOrganisation> publicDomain(
-            final Map<String, Organisation> organisations, final Element parent) {
-        return new JDomBuilder().collectByOrganisations(parent.getChild("public-domain"), organisations);
-    }
-
-    /**
-     * Builds a within directory model from XML.
-     * @param element not null
-     * @param licenses not null
-     * @param organisations not null
-     * @return not null
-     */
-    public WithinDirectory withinDirectory(Element element,
-            Map<String, License> licenses,
-            Map<String, Organisation> organisations) {
-        return new WithinDirectory(element.getAttributeValue("dir"), 
-                withLicenses(licenses, organisations, element), publicDomain(organisations, element));
-    }
-
-    /**
-     * Collects organisation definitions within document.
-     * @param document, not null
-     * @return organisations indexed by id, not null possibly empty
-     */
-    public Map<String, Organisation> mapOrganisations(Document document) {
-        final Map<String, Organisation> organisationsById = new HashMap<String, Organisation>();
-        
-        final Element childOrganisations = document.getRootElement().getChild("organisations");
-        if (childOrganisations != null) {
-            @SuppressWarnings("unchecked")
-            final List<Element> organisations = (List<Element>) childOrganisations.getChildren("organisation");
-            for (final Element element: organisations) {
-                new JDomBuilder().organisation(element).storeIn(organisationsById);
-            }
-        }
-        return Collections.unmodifiableMap(organisationsById);
-    }
-
-    /**
-     * Collects license definitions within document.
-     * @param document, not null
-     * @return licenses, indexed by id, not null, possibly empty
-     */
-    public Map<String, License> mapLicenses(Document document) {
-        final Map<String, License> results = new HashMap<String, License>();
-        final Element licensesChild = document.getRootElement().getChild("licenses");
-        if (licensesChild != null) {
-            @SuppressWarnings("unchecked")
-            final List<Element> children = (List<Element>) licensesChild.getChildren();
-            for (final Element element: children) {
-                new JDomBuilder().license(element).storeIn(results);
-            }
-        }
-        return Collections.unmodifiableMap(results);
-
-    }
-
-    /**
-     * Finds the primary license for the given document from the given licenses.
-     * @param document not null
-     * @param licenses not null
-     * @return not null
-     */
-    public License primaryLicense(Document document,
-            Map<String, License> licenses) {
-        final String idAttributeValue = getPrimaryLicenseElement(document).getAttributeValue("id");
-        final License results = licenses.get(idAttributeValue);
-        if (results == null) {
-            throw new MissingIDException(LICENSE_ELEMENT_NAME, PRIMARY_LICENSE_NAME, idAttributeValue);
-        }
-        return results;
-    }
-
-    /**
-     * Gets the element representing the primary license.
-     * @param document not null
-     * @return not null
-     */
-    private Element getPrimaryLicenseElement(final Document document) {
-        return document.getRootElement().getChild(PRIMARY_LICENSE_NAME);
-    }
-
-    /**
-     * Gets the additional primary copyright notice 
-     * from the document.
-     * @param document not null
-     * @return optional primary copyright notice, possibly null
-     */
-    public String primaryCopyrightNotice(final Document document) {
-        final String result;
-        final Element copyrightElement = 
-                getPrimaryLicenseElement(document).getChild(COPYRIGHT_NOTICE_NAME);
-        if (copyrightElement == null) {
-            result = null;
-        } else {
-            result = copyrightElement.getTextTrim();
-        }
-        return result;
-    }
-
-    
-    /**
-     * Collects notices in the given documents.
-     * @param document, not null
-     * @return notices indexed by id, immutable, not null, possibly empty
-     */
-    public Map<String, String> mapNotices(Document document) {
-        final Map<String, String> results = new HashMap<String, String>();
-        final Element noticesElement = document.getRootElement().getChild("notices");
-        if (noticesElement != null){
-            @SuppressWarnings("unchecked")
-            final List<Element> children = (List<Element>) noticesElement.getChildren();
-            for (final Element element: children) {
-                results.put(element.getAttributeValue("id"), element.getTextTrim());
-            }
-        }
-        return Collections.unmodifiableMap(results);
-
-    }
-
-    /**
-     * Retrieves the text of the primary notice.
-     * @param document, not null
-     * @return the text of the primary notice, 
-     * or null when there is no primary notice
-     */
-    public String primaryNotice(Document document) {
-        final String result;
-        final Element primaryNoticeElement = document.getRootElement().getChild("primary-notice");
-        if (primaryNoticeElement == null) {
-            result = null;
-        } else {
-            result = primaryNoticeElement.getText()
-                .replace("${year}", Integer.toString(Calendar.getInstance().get(Calendar.YEAR)));
-        }
-        return result;
-    }
-
-    /**
-     * Retrieves the ID of the primary organisation.
-     * @param document, not null
-     * @return the id of the primary organisation when set,
-     * otherwise null
-     */
-    public String primaryOrganisationId(final Document document) {
-        final String result;
-        final Element primaryOrganisationElement = document.getRootElement().getChild("primary-organisation");
-        if (primaryOrganisationElement == null) {
-            result = null;
-        } else {
-            result = primaryOrganisationElement.getAttributeValue("id");
-        }
-        return result;
-    }    
-
-    private WithinDirectory directory(final Element element, final Map<String, License> licenses,
-            final Map<String, Organisation> organisations) {
-        return new JDomBuilder().withinDirectory(element, licenses, organisations);
-    }
-
-    /**
-     * Collects contents of the document.
-     * @param document not null
-     * @return not null, possibly empty
-     * @throws DuplicateElementException when dir names are not unique
-     */
-    @SuppressWarnings("PMD.EmptyIfStmt")
-    public Collection<WithinDirectory> collectContents(final Document document, final Map<String, License> licenses,
-            final Map<String, Organisation> organisations) throws DuplicateElementException {
-        final Collection<WithinDirectory> results = new TreeSet<WithinDirectory>();
-        @SuppressWarnings("unchecked")
-        final List<Element> children = document.getRootElement().getChildren("within");
-        for (Element element: children) {
-            if (results.add(directory(element, licenses, organisations))) {
-                // fine
-            } else {
-                throw new DuplicateElementException("Duplicate parameter '" + element.getAttribute("dir").getValue() + "'");
-            }
-        }
-        return results;
-    }
-    
-    /**
-     * Builds work from the given document.
-     * @param document not null
-     * @return not null
-     */
-    public Descriptor build(final Document document) {
-        final Map<String, Organisation> organisations = mapOrganisations(document);
-        final Map<String, License> licenses = mapLicenses(document);
-        final Map<String, String> notices = mapNotices(document);
-        final License primaryLicense = primaryLicense(document, licenses);
-        final String primaryCopyrightNotice = primaryCopyrightNotice(document);
-        final String primaryNotice = primaryNotice(document);
-        final String primaryOrganisationId = primaryOrganisationId(document);
-        final Collection<WithinDirectory> contents = collectContents(document, licenses, organisations); 
-        return new Descriptor(primaryLicense, primaryCopyrightNotice,
-                primaryOrganisationId, primaryNotice, 
-                licenses, notices, organisations, contents);
-    }
-    
-    public Descriptor build(final InputStream xmlStream) throws JDOMException, IOException {
-        return build(new SAXBuilder().build(xmlStream));
-    }
+	private static final String PRIMARY_LICENSE_NAME = "primary-license";
+	/** Names the element representing an organisation */
+	private static final String ORGANISATION_ELEMENT_NAME = "organisation";
+	/** Names the element representing a resource */
+	private static final String RESOURCE_ELEMENT_NAME = "resource";
+
+	/**
+	 * Builds a resource.
+	 * 
+	 * @param element
+	 *            not null
+	 * @return built resource, not null
+	 * @throws UnexpectedElementException
+	 *             when element is not named 'resource'
+	 */
+	public Resource resource(Element element) throws UnexpectedElementException {
+		if (RESOURCE_ELEMENT_NAME.equals(element.getName())) {
+			return new Resource(StringUtils.trim(element
+					.getAttributeValue("name")), StringUtils.trim(element
+					.getAttributeValue("notice")), StringUtils.trim(element
+					.getAttributeValue("source")));
+		} else {
+			throw unexpectedElementException(element, RESOURCE_ELEMENT_NAME);
+		}
+	}
+
+	/**
+	 * Builds a suitable exception when the element name is unexpected.
+	 * 
+	 * @param element
+	 *            , not null
+	 * @param expectedElement
+	 *            , not null
+	 * @return a suitable exception, not null
+	 */
+	private UnexpectedElementException unexpectedElementException(
+			Element element, final String expectedElement) {
+		return new UnexpectedElementException(expectedElement,
+				element.getName());
+	}
+
+	/**
+	 * Builds an organisation model from xml.
+	 * 
+	 * @param element
+	 *            , not null
+	 * @return {@link Organisation} not null
+	 * @throws UnexpectedElementException
+	 *             when element is not named 'organisation'
+	 */
+	public Organisation organisation(Element element)
+			throws UnexpectedElementException {
+		if (ORGANISATION_ELEMENT_NAME.equals(element.getName())) {
+			return new Organisation(element.getAttributeValue("id"),
+					element.getAttributeValue("name"),
+					element.getAttributeValue("url"));
+		} else {
+			throw unexpectedElementException(element, ORGANISATION_ELEMENT_NAME);
+		}
+	}
+
+	/**
+	 * @param element
+	 *            JDOM element to collect.
+	 * @return collected resources.
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection<Resource> collectResources(Element element) {
+		final Collection<Resource> resources = new TreeSet<Resource>();
+		for (Element resourceElement : (List<Element>) element
+				.getChildren("resource")) {
+			resources.add(new JDomBuilder().resource(resourceElement));
+		}
+		return Collections.unmodifiableCollection(resources);
+	}
+
+	/**
+	 * @return Finds the organisation linked by ID from the given element.
+	 * @param element
+	 *            modelled ByOrganisation, not null
+	 * @param organisationsById
+	 *            organisations identified, not null
+	 * @throws MissingIDException
+	 *             when the linked organisation is not found in the given map
+	 */
+	public Organisation organisation(final Element element,
+			final Map<String, Organisation> organisationsById)
+			throws MissingIDException {
+		final String id = element.getAttributeValue("id");
+		if (organisationsById.containsKey(id)) {
+			return organisationsById.get(id);
+		} else {
+			throw new MissingIDException(ORGANISATION_ELEMENT_NAME,
+					element.getName(), id);
+		}
+	}
+
+	/**
+	 * Builds a by-organisation model from xml.
+	 * 
+	 * @param element
+	 *            not null
+	 * @param organisation
+	 *            not null
+	 * @return not null
+	 */
+	public ByOrganisation byOrganisation(final Element element,
+			final Organisation organisation) {
+		return new ByOrganisation(organisation, collectResources(element));
+	}
+
+	/**
+	 * Builds a by-organisation model from xml.
+	 * 
+	 * @param byOrganisation
+	 *            not null
+	 * @param organisationsById
+	 *            not null
+	 * @return not null
+	 * @throws MissingIDException
+	 *             when the linked organisation is not found in the given map
+	 */
+	public ByOrganisation byOrganisation(final Element byOrganisation,
+			final Map<String, Organisation> organisationsById)
+			throws MissingIDException {
+		return byOrganisation(byOrganisation,
+				organisation(byOrganisation, organisationsById));
+	}
+
+	/**
+	 * Collects by-organisation children.
+	 * 
+	 * @param parent
+	 *            not null
+	 * @param map
+	 *            not null
+	 * @return unmodifiable set sort by natural order, not null
+	 */
+	@SuppressWarnings("unchecked")
+	public SortedSet<ByOrganisation> collectByOrganisations(
+			final Element parent, final Map<String, Organisation> map) {
+		final SortedSet<ByOrganisation> results = new TreeSet<ByOrganisation>();
+		if (parent != null) {
+			for (final Element byOrgElement : (List<Element>) parent
+					.getChildren("by-organisation")) {
+				results.add(byOrganisation(byOrgElement, map));
+			}
+		}
+		return Collections.unmodifiableSortedSet(results);
+	}
+
+	/**
+	 * Builds a license model from xml.
+	 * 
+	 * @param element
+	 *            not null
+	 * @return not null
+	 */
+	public License license(Element element) {
+		final Element text = element.getChild("text");
+		return new License("yes".equalsIgnoreCase(element
+				.getAttributeValue("requires-source")), text == null ? ""
+				: text.getText(), expectedParameters(element),
+				element.getAttributeValue("id"),
+				element.getAttributeValue("url"),
+				element.getAttributeValue("name"));
+	}
+
+	@SuppressWarnings("unchecked")
+	private Collection<String> expectedParameters(final Element element) {
+		final Collection<String> results = new HashSet<String>();
+		final Element templateElement = element.getChild("template");
+		if (templateElement != null) {
+			for (Element parameterNameElement : (List<Element>) templateElement
+					.getChildren("parameter-name")) {
+				results.add(parameterNameElement.getTextTrim());
+			}
+		}
+		return results;
+	}
+
+	/**
+	 * Finds the license with an id matching that referenced by the element.
+	 * 
+	 * @param element
+	 *            not null
+	 * @param licenses
+	 *            not null
+	 * @return not null
+	 * @throws MissingIDException
+	 *             when referenced license isn't found in the collection
+	 */
+	public License license(final Element element,
+			final Map<String, License> licenses) throws MissingIDException {
+		final String id = element.getAttributeValue("id");
+		if (licenses.containsKey(id)) {
+			return licenses.get(id);
+		} else {
+			throw new MissingIDException(LICENSE_ELEMENT_NAME,
+					element.getName(), id);
+		}
+	}
+
+	/**
+	 * @return Builds a with-license model from xml.
+	 * @param element
+	 *            not null
+	 * @param licenses
+	 *            not null
+	 * @param organisations
+	 *            not null
+	 * 
+	 * @throws MissingIDException
+	 *             when referenced license isn't found in the collection
+	 */
+	public WithLicense withLicense(Element element,
+			Map<String, License> licenses,
+			Map<String, Organisation> organisations) throws MissingIDException {
+		return new WithLicense(license(element, licenses),
+				copyrightNotice(element), parameters(element),
+				collectByOrganisations(element, organisations));
+	}
+
+	/**
+	 * Extracts copyright notice content from with-license.
+	 * 
+	 * @param element
+	 *            not null
+	 * @return not null
+	 */
+	private String copyrightNotice(final Element element) {
+		final String result;
+		final Element copyrightNoticeElement = element
+				.getChild(COPYRIGHT_NOTICE_NAME);
+		if (copyrightNoticeElement == null) {
+			result = null;
+		} else {
+			result = copyrightNoticeElement.getTextTrim();
+		}
+		return result;
+	}
+
+	/**
+	 * Builds a list of parameter values by name.
+	 * 
+	 * @param element
+	 *            not null
+	 * @return parameter values indexed by value, not null
+	 * @throws DuplicateElementException
+	 *             when two parameters shared the same name
+	 */
+	@SuppressWarnings("unchecked")
+	public Map<String, String> parameters(Element element)
+			throws DuplicateElementException {
+		final Map<String, String> results = new HashMap<String, String>();
+		final Element licenseParametersElement = element
+				.getChild("license-parameters");
+		if (licenseParametersElement != null) {
+			for (Element parameterElement : (List<Element>) licenseParametersElement
+					.getChildren("parameter")) {
+				final String name = parameterElement.getChild("name")
+						.getTextTrim();
+				if (results.containsKey(name)) {
+					throw new DuplicateElementException("Duplicate parameter '"
+							+ name + "'");
+				}
+				results.put(name, parameterElement.getChild("value")
+						.getTextTrim());
+			}
+		}
+		return results;
+	}
+
+	/**
+	 * Collects child with-licenses.
+	 * 
+	 * @param licenses
+	 *            not null
+	 * @param organisations
+	 *            not null
+	 * @param parent
+	 *            not null
+	 * @return not null, possibly empty
+	 */
+	@SuppressWarnings("unchecked")
+	public Collection<WithLicense> withLicenses(Map<String, License> licenses,
+			Map<String, Organisation> organisations, Element parent) {
+		final List<WithLicense> results = new ArrayList<WithLicense>();
+		for (Element withLicenseElement : (List<Element>) parent
+				.getChildren("with-license")) {
+			results.add(new JDomBuilder().withLicense(withLicenseElement,
+					licenses, organisations));
+		}
+		Collections.sort(results);
+		return results;
+	}
+
+	/**
+	 * Collects child organisations of public domain.
+	 * 
+	 * @param organisations
+	 *            not null
+	 * @param parent
+	 *            not null
+	 * @return not null, possibly null
+	 */
+	public Collection<ByOrganisation> publicDomain(
+			final Map<String, Organisation> organisations, final Element parent) {
+		return new JDomBuilder().collectByOrganisations(
+				parent.getChild("public-domain"), organisations);
+	}
+
+	/**
+	 * Builds a within directory model from XML.
+	 * 
+	 * @param element
+	 *            not null
+	 * @param licenses
+	 *            not null
+	 * @param organisations
+	 *            not null
+	 * @return not null
+	 */
+	public WithinDirectory withinDirectory(Element element,
+			Map<String, License> licenses,
+			Map<String, Organisation> organisations) {
+		return new WithinDirectory(element.getAttributeValue("dir"),
+				withLicenses(licenses, organisations, element), publicDomain(
+						organisations, element));
+	}
+
+	/**
+	 * Collects organisation definitions within document.
+	 * 
+	 * @param document
+	 *            , not null
+	 * @return organisations indexed by id, not null possibly empty
+	 */
+	public Map<String, Organisation> mapOrganisations(Document document) {
+		final Map<String, Organisation> organisationsById = new HashMap<String, Organisation>();
+
+		final Element childOrganisations = document.getRootElement().getChild(
+				"organisations");
+		if (childOrganisations != null) {
+			@SuppressWarnings("unchecked")
+			final List<Element> organisations = (List<Element>) childOrganisations
+					.getChildren("organisation");
+			for (final Element element : organisations) {
+				new JDomBuilder().organisation(element).storeIn(
+						organisationsById);
+			}
+		}
+		return Collections.unmodifiableMap(organisationsById);
+	}
+
+	/**
+	 * Collects license definitions within document.
+	 * 
+	 * @param document
+	 *            , not null
+	 * @return licenses, indexed by id, not null, possibly empty
+	 */
+	public Map<String, License> mapLicenses(Document document) {
+		final Map<String, License> results = new HashMap<String, License>();
+		final Element licensesChild = document.getRootElement().getChild(
+				"licenses");
+		if (licensesChild != null) {
+			@SuppressWarnings("unchecked")
+			final List<Element> children = (List<Element>) licensesChild
+					.getChildren();
+			for (final Element element : children) {
+				new JDomBuilder().license(element).storeIn(results);
+			}
+		}
+		return Collections.unmodifiableMap(results);
+
+	}
+
+	/**
+	 * Finds the primary license for the given document from the given licenses.
+	 * 
+	 * @param document
+	 *            not null
+	 * @param licenses
+	 *            not null
+	 * @return not null
+	 */
+	public License primaryLicense(Document document,
+			Map<String, License> licenses) {
+		final String idAttributeValue = getPrimaryLicenseElement(document)
+				.getAttributeValue("id");
+		final License results = licenses.get(idAttributeValue);
+		if (results == null) {
+			throw new MissingIDException(LICENSE_ELEMENT_NAME,
+					PRIMARY_LICENSE_NAME, idAttributeValue);
+		}
+		return results;
+	}
+
+	/**
+	 * Gets the element representing the primary license.
+	 * 
+	 * @param document
+	 *            not null
+	 * @return not null
+	 */
+	private Element getPrimaryLicenseElement(final Document document) {
+		return document.getRootElement().getChild(PRIMARY_LICENSE_NAME);
+	}
+
+	/**
+	 * Gets the additional primary copyright notice from the document.
+	 * 
+	 * @param document
+	 *            not null
+	 * @return optional primary copyright notice, possibly null
+	 */
+	public String primaryCopyrightNotice(final Document document) {
+		final String result;
+		final Element copyrightElement = getPrimaryLicenseElement(document)
+				.getChild(COPYRIGHT_NOTICE_NAME);
+		if (copyrightElement == null) {
+			result = null;
+		} else {
+			result = copyrightElement.getTextTrim();
+		}
+		return result;
+	}
+
+	/**
+	 * Collects notices in the given documents.
+	 * 
+	 * @param document
+	 *            , not null
+	 * @return notices indexed by id, immutable, not null, possibly empty
+	 */
+	public Map<String, String> mapNotices(Document document) {
+		final Map<String, String> results = new HashMap<String, String>();
+		final Element noticesElement = document.getRootElement().getChild(
+				"notices");
+		if (noticesElement != null) {
+			@SuppressWarnings("unchecked")
+			final List<Element> children = (List<Element>) noticesElement
+					.getChildren();
+			for (final Element element : children) {
+				results.put(element.getAttributeValue("id"),
+						element.getTextTrim());
+			}
+		}
+		return Collections.unmodifiableMap(results);
+
+	}
+
+	/**
+	 * Retrieves the text of the primary notice.
+	 * 
+	 * @param document
+	 *            , not null
+	 * @return the text of the primary notice, or null when there is no primary
+	 *         notice
+	 */
+	public String primaryNotice(Document document) {
+		final String result;
+		final Element primaryNoticeElement = document.getRootElement()
+				.getChild("primary-notice");
+		if (primaryNoticeElement == null) {
+			result = null;
+		} else {
+			result = primaryNoticeElement.getText()
+					.replace(
+							"${year}",
+							Integer.toString(Calendar.getInstance().get(
+									Calendar.YEAR)));
+		}
+		return result;
+	}
+
+	/**
+	 * Retrieves the ID of the primary organisation.
+	 * 
+	 * @param document
+	 *            , not null
+	 * @return the id of the primary organisation when set, otherwise null
+	 */
+	public String primaryOrganisationId(final Document document) {
+		final String result;
+		final Element primaryOrganisationElement = document.getRootElement()
+				.getChild("primary-organisation");
+		if (primaryOrganisationElement == null) {
+			result = null;
+		} else {
+			result = primaryOrganisationElement.getAttributeValue("id");
+		}
+		return result;
+	}
+
+	private WithinDirectory directory(final Element element,
+			final Map<String, License> licenses,
+			final Map<String, Organisation> organisations) {
+		return new JDomBuilder().withinDirectory(element, licenses,
+				organisations);
+	}
+
+	/**
+	 * Collects contents of the document.
+	 * 
+	 * @param document
+	 *            not null
+	 * @param licenses
+	 *            not null
+	 * @param organisations
+	 *            not null
+	 * @return not null, possibly empty
+	 * @throws DuplicateElementException
+	 *             when directory names are not unique
+	 */
+	public Collection<WithinDirectory> collectContents(final Document document,
+			final Map<String, License> licenses,
+			final Map<String, Organisation> organisations)
+			throws DuplicateElementException {
+		final Collection<WithinDirectory> results = new TreeSet<WithinDirectory>();
+		@SuppressWarnings("unchecked")
+		final List<Element> children = document.getRootElement().getChildren(
+				"within");
+		for (Element element : children) {
+			boolean addedSuccessfully = results.add(directory(element,
+					licenses, organisations));
+
+			if (!addedSuccessfully) {
+				throw new DuplicateElementException("Duplicate parameter '"
+						+ element.getAttribute("dir").getValue() + "'");
+			}
+		}
+		return results;
+	}
+
+	/**
+	 * Builds work from the given document.
+	 * 
+	 * @param document
+	 *            not null
+	 * @return not null
+	 */
+	public Descriptor build(final Document document) {
+		final Map<String, Organisation> organisations = mapOrganisations(document);
+		final Map<String, License> licenses = mapLicenses(document);
+		final Map<String, String> notices = mapNotices(document);
+		final License primaryLicense = primaryLicense(document, licenses);
+		final String primaryCopyrightNotice = primaryCopyrightNotice(document);
+		final String primaryNotice = primaryNotice(document);
+		final String primaryOrganisationId = primaryOrganisationId(document);
+		final Collection<WithinDirectory> contents = collectContents(document,
+				licenses, organisations);
+		return new Descriptor(primaryLicense, primaryCopyrightNotice,
+				primaryOrganisationId, primaryNotice, licenses, notices,
+				organisations, contents);
+	}
+
+	public Descriptor build(final InputStream xmlStream) throws JDOMException,
+			IOException {
+		return build(new SAXBuilder().build(xmlStream));
+	}
 }

Modified: creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/MissingIDException.java
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/MissingIDException.java?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/MissingIDException.java (original)
+++ creadur/whisker/trunk/apache-whisker-xml/src/main/java/org/apache/creadur/whisker/fromxml/MissingIDException.java Wed Aug  6 21:16:30 2014
@@ -30,8 +30,8 @@ public class MissingIDException extends 
     
     /**
      * Constructs an instance.
-     * @param organisationElementName not null
-     * @param name not null
+     * @param linkedElement not null
+     * @param linkingElement not null
      * @param id not null
      */
     public MissingIDException(final String linkedElement, final String linkingElement,

Modified: creadur/whisker/trunk/pom.xml
URL: http://svn.apache.org/viewvc/creadur/whisker/trunk/pom.xml?rev=1616351&r1=1616350&r2=1616351&view=diff
==============================================================================
--- creadur/whisker/trunk/pom.xml (original)
+++ creadur/whisker/trunk/pom.xml Wed Aug  6 21:16:30 2014
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.apache</groupId>
     <artifactId>apache</artifactId>
-    <version>13</version>
+    <version>14</version>
   </parent>
 
   <dependencyManagement>
@@ -49,7 +49,7 @@
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
-        <version>3.0</version>
+        <version>3.3.2</version>
       </dependency>
 
       <dependency>
@@ -61,7 +61,7 @@
       <dependency>
           <groupId>commons-io</groupId>
           <artifactId>commons-io</artifactId>
-          <version>2.1</version>
+          <version>2.4</version>
       </dependency>
 
       <dependency>
@@ -85,7 +85,7 @@
       <dependency>
         <groupId>org.jmock</groupId>
         <artifactId>jmock-junit3</artifactId>
-        <version>2.5.1</version>
+        <version>2.6.0</version>
         <scope>test</scope>
       </dependency>
 
@@ -106,7 +106,7 @@
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <target.jdk>1.5</target.jdk>
-    <junit.version>3.8.1</junit.version>
+    <junit.version>3.8.2</junit.version>
     <!--
     Publication to the website is a two phase operation:
 
@@ -132,6 +132,7 @@
 
   <!-- ================================ Build information -->
   <build>
+  <defaultGoal>clean install</defaultGoal>
     <pluginManagement>
       <plugins>
         <plugin>
@@ -145,7 +146,7 @@
         <plugin>
 	        <groupId>org.apache.maven.plugins</groupId>
 	        <artifactId>maven-site-plugin</artifactId>
-	        <version>3.1</version>
+	        <version>3.4</version>
 	        <configuration>
 	          <outputEncoding>${project.build.sourceEncoding}</outputEncoding>
 	          <inputEncoding>${project.build.sourceEncoding}</inputEncoding>
@@ -156,17 +157,17 @@
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-checkstyle-plugin</artifactId>
-	              <version>2.9.1</version>
+	              <version>2.12.1</version>
 	            </plugin>
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-javadoc-plugin</artifactId>
-	              <version>2.9</version>
+	              <version>2.9.1</version>
 	            </plugin>
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-jxr-plugin</artifactId>
-	              <version>2.3</version>
+	              <version>2.4</version>
 	              <configuration>
 	                <aggregate>true</aggregate>
 	                <linkJavadoc>true</linkJavadoc>
@@ -175,11 +176,11 @@
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-surefire-report-plugin</artifactId>
-	              <version>2.12</version>
+	              <version>2.17</version>
 	            </plugin>
 	            <plugin>
 	              <artifactId>maven-pmd-plugin</artifactId>
-	              <version>2.7.1</version>
+	              <version>3.1</version>
 	              <configuration>
 	                <linkXRef>true</linkXRef>
 	                <sourceEncoding>utf-8</sourceEncoding>
@@ -195,7 +196,7 @@
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-changes-plugin</artifactId>
-	              <version>2.7.1</version>
+	              <version>2.10</version>
 	              <reportSets>
 	                <reportSet>
 	                  <reports>
@@ -208,13 +209,13 @@
 	            <plugin>
 	              <groupId>org.codehaus.mojo</groupId>
 	              <artifactId>jdepend-maven-plugin</artifactId>
-	              <version>2.0-beta-2</version>
+	              <version>2.0</version>
 	            </plugin>
 	            <!-- cobertura plugin -->
 	            <plugin>
 	              <groupId>org.codehaus.mojo</groupId>
 	              <artifactId>cobertura-maven-plugin</artifactId>
-	              <version>2.5.1</version>
+	              <version>2.6</version>
 	            </plugin>
 	            <!-- catch code tags -->
 	            <plugin>
@@ -239,12 +240,12 @@
 	            <plugin>
 	              <groupId>org.codehaus.mojo</groupId>
 	              <artifactId>findbugs-maven-plugin</artifactId>
-	              <version>2.5</version>
+	              <version>3.0.0</version>
 	            </plugin>
 	            <plugin>
 	              <groupId>org.apache.maven.plugins</groupId>
 	              <artifactId>maven-project-info-reports-plugin</artifactId>
-	              <version>2.4</version>
+	              <version>2.7</version>
 	            </plugin>
 
 	          </reportPlugins>
@@ -266,7 +267,7 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-scm-publish-plugin</artifactId>
-            <version>1.0-beta-2</version>
+            <version>1.1</version>
             <extensions>true</extensions>
             <configuration>
               <!-- svn location for publication -->
@@ -281,7 +282,7 @@
       <extension>
         <groupId>org.apache.maven.wagon</groupId>
          <artifactId>wagon-ssh</artifactId>
-         <version>1.0-beta-7</version>
+         <version>2.6</version>
       </extension>
     </extensions>
   </build>
@@ -315,6 +316,16 @@
     <system>JIRA</system>
     <url>https://issues.apache.org/jira/browse/WHISKER</url>
   </issueManagement>
+  
+	<!-- TODO
+	<ciManagement>
+	        <system>Jenkins</system>
+	        <url>http://ci.apache.org/builders/whisker_trunk</url>
+	        Jenkins builds are at:
+	        https://builds.apache.org/job/Creadur-Whisker/ - check Java5 compliance and run all tests
+	        https://builds.apache.org/job/Creadur-Whisker-Site/ - generate mvn site
+	</ciManagement>
+	 -->
 
   <mailingLists>
     <mailingList>