001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.batch;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.util.List;
025    import java.util.Map;
026    
027    import org.apache.maven.project.MavenProject;
028    import org.sonar.api.batch.bootstrap.ProjectDefinition;
029    import org.sonar.api.utils.SonarException;
030    
031    import com.google.common.collect.Maps;
032    
033    public final class MavenProjectConverter {
034    
035      private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
036          " Probably you use Maven Advanced Reactor Options, which is not supported by Sonar and should not be used.";
037    
038      public static ProjectDefinition convert(List<MavenProject> poms, MavenProject root) {
039        Map<String, MavenProject> paths = Maps.newHashMap(); // projects by canonical path to pom.xml
040        Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
041    
042        try {
043          for (MavenProject pom : poms) {
044            paths.put(pom.getFile().getCanonicalPath(), pom);
045            defs.put(pom, convert(pom));
046          }
047    
048          for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
049            MavenProject pom = entry.getValue();
050            for (Object m : pom.getModules()) {
051              String moduleId = (String) m;
052              File modulePath = new File(pom.getBasedir(), moduleId);
053              if (modulePath.exists() && modulePath.isDirectory()) {
054                modulePath = new File(modulePath, "pom.xml");
055              }
056              MavenProject module = paths.get(modulePath.getCanonicalPath());
057    
058              ProjectDefinition parentProject = defs.get(pom);
059              ProjectDefinition subProject = defs.get(module);
060              if (parentProject == null) {
061                throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
062              }
063              if (subProject == null) {
064                throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
065              }
066              parentProject.addSubProject(subProject);
067            }
068          }
069        } catch (IOException e) {
070          throw new SonarException(e);
071        }
072    
073        ProjectDefinition rootProject = defs.get(root);
074        if (rootProject == null) {
075          throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
076        }
077    
078        return rootProject;
079      }
080    
081      /**
082       * Visibility has been relaxed for tests.
083       */
084      static ProjectDefinition convert(MavenProject pom) {
085        String key = new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
086        ProjectDefinition definition = ProjectDefinition.create();
087        // IMPORTANT NOTE : reference on properties from POM model must not be saved, instead they should be copied explicitly - see SONAR-2896
088        definition
089            .setProperties(pom.getModel().getProperties())
090            .setKey(key)
091            .setVersion(pom.getVersion())
092            .setName(pom.getName())
093            .setDescription(pom.getDescription())
094            .addContainerExtension(pom);
095        synchronizeFileSystem(pom, definition);
096        return definition;
097      }
098    
099      public static void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
100        into.setBaseDir(pom.getBasedir());
101        into.setWorkDir(new File(resolvePath(pom.getBuild().getDirectory(), pom.getBasedir()), "sonar"));
102        into.setSourceDirs((String[]) pom.getCompileSourceRoots().toArray(new String[pom.getCompileSourceRoots().size()]));
103        into.setTestDirs((String[]) pom.getTestCompileSourceRoots().toArray(new String[pom.getTestCompileSourceRoots().size()]));
104      }
105    
106      static File resolvePath(String path, File basedir) {
107        File file = new File(path);
108        if (!file.isAbsolute()) {
109          try {
110            file = new File(basedir, path).getCanonicalFile();
111          } catch (IOException e) {
112            throw new SonarException("Unable to resolve path '" + path + "'", e);
113          }
114        }
115        return file;
116      }
117    
118      private MavenProjectConverter() {
119        // only static methods
120      }
121    }