/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.model.ext.maven.internal;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.skalli.model.Issuer;
import org.eclipse.skalli.model.ValidationException;
import org.eclipse.skalli.model.ext.devinf.DevInfProjectExt;
import org.eclipse.skalli.model.ext.maven.MavenModule;
import org.eclipse.skalli.model.ext.maven.MavenPomResolver;
import org.eclipse.skalli.model.ext.maven.MavenReactor;
public class MavenResolver implements Issuer {
protected final UUID project;
protected final MavenPomResolver pomResolver;
/**
* Creates a resolver for a given project.
* @param project the unique identifier of the project for which reactor information is to be calculated.
* @param pomResolver the path resolver to use to convert resource paths to download URLs.
*/
public MavenResolver(UUID project, MavenPomResolver pomResolver) {
this.project = project;
this.pomResolver = pomResolver;
}
/**
* Resolves a Maven reactor project and its modules.
*
* Note, this method assumes, that the POM files downloaded from the SCM system are
* syntactically and semantically correct and complete. No attempt is made to validate
* the returned <code>MavenReactor</code> instance. It may therefore contain incomplete
* or invalid information.
*
* @param reactorPomPath the path relative to the repository root of the reactor POM file
* (without leading or trailing slashes and without file namne).
* @param scmLocation the SCM location provided by the
* project (see {@link DevInfProjectExt#getScmLocation()}.
*
* @throws IOException if an i/o error occured, e.g. the connection to the server
* providing POM files cannot be established or is lost.
* @throws ValidationException if any of the relevant POMs is invalid or cannot be parsed.
* @throws IllegalArgumentException if the given SCM location cannot be resolved by
* the path resolver assigned to this <code>MavenResolver</code> instance.
*/
public MavenReactor resolve(String scmLocation, String reactorPomPath)
throws IOException, ValidationException {
MavenReactor mavenReactor = new MavenReactor();
MavenPom reactorPom = pomResolver.getMavenPom(project, scmLocation, reactorPomPath);
if (reactorPom == null) {
throw new ValidationException(MessageFormat.format(
"no pom for scm location {0} and reactorPomPath {1}", scmLocation, reactorPomPath));
}
MavenModule parent = reactorPom.getParent();
MavenModule self = getSelf(reactorPom, parent);
mavenReactor.setCoordinate(self);
Set<String> moduleTags = reactorPom.getModuleTags();
for (String moduleTag : moduleTags) {
String normalizedPath = getNormalizedPath(reactorPomPath, moduleTag);
if (normalizedPath != null) {
List<String> visitedPaths = new ArrayList<String>();
visitedPaths.add(normalizedPath);
mavenReactor.addModules(getModules(visitedPaths, scmLocation, normalizedPath, self));
}
}
return mavenReactor;
}
private Set<MavenModule> getModules(List<String> visitedPaths, String scmLocation,
String relativePath, MavenModule parent)
throws IOException, ValidationException {
TreeSet<MavenModule> result = new TreeSet<MavenModule>();
MavenPom modulePom = pomResolver.getMavenPom(project, scmLocation, relativePath);
if (modulePom == null) {
return result;
}
MavenModule self = getSelf(modulePom, parent);
result.add(self);
Set<String> moduleTags = modulePom.getModuleTags();
for (String moduleTag : moduleTags) {
String normalizedPath = getNormalizedPath(relativePath, moduleTag);
if (normalizedPath != null && !visitedPaths.contains(normalizedPath)) {
visitedPaths.add(normalizedPath);
result.addAll(getModules(visitedPaths, scmLocation, normalizedPath, self));
}
}
return result;
}
/**
* Concats <code>pathPrefix</code> and <code>path</code>, normalizes the result by
* removing double and single dot path segments, converts all file separators to forward slashes
* and removes a leading slash, if any.
*
* @param pathPrefix the path prefix.
* @param path the path relative to the path prefix.
* @return the bnormalized path, or <code>null</code> of removing double and single dot path
* segments yielded an invalid path, e.g. a path like <tt>"foo/../../bar"</tt> would be treated
* as invalid.
*/
private String getNormalizedPath(String pathPrefix, String path) {
String normalizedPath = FilenameUtils.normalize(pathPrefix + "/" + path); //$NON-NLS-1$
if (normalizedPath == null) {
return null;
}
normalizedPath = FilenameUtils.separatorsToUnix(normalizedPath);
if (normalizedPath.charAt(0) == '/') {
normalizedPath = normalizedPath.substring(1);
}
return normalizedPath;
}
private MavenModule getSelf(MavenPom mavenPom, MavenModule parent) {
MavenModule self = mavenPom.getSelf();
if (parent != null) {
if (self.getGroupId() == null) {
self.setGroupId(parent.getGroupId());
}
}
return self;
}
}