package org.springframework.roo.project.maven; import static org.springframework.roo.project.maven.Pom.DEFAULT_PACKAGING; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.springframework.roo.project.Dependency; import org.springframework.roo.project.Filter; import org.springframework.roo.project.Path; import org.springframework.roo.project.Plugin; import org.springframework.roo.project.Property; import org.springframework.roo.project.Repository; import org.springframework.roo.project.Resource; import org.springframework.roo.project.packaging.PackagingProvider; import org.springframework.roo.project.packaging.PackagingProviderRegistry; import org.springframework.roo.support.util.FileUtils; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Element; @Component @Service public class PomFactoryImpl implements PomFactory { private static final String ARTIFACT_ID_XPATH = "/project/artifactId"; private static final String DEFAULT_RELATIVE_PATH = "../pom.xml"; private static final String DEPENDENCY_XPATH = "/project/dependencies/dependency"; private static final String DEPENDENCY_IN_DEPENDENCY_MANAGEMENT_XPATH = "/project/dependencyManagement/dependencies/dependency"; private static final String FILTER_XPATH = "/project/build/filters/filter"; private static final String GROUP_ID_XPATH = "/project/groupId"; private static final String MODULE_XPATH = "/project/modules/module"; private static final String NAME_XPATH = "/project/name"; private static final String PACKAGING_PROVIDER_PROPERTY_XPATH = "/project/properties/roo.packaging.provider"; private static final String PACKAGING_XPATH = "/project/packaging"; private static final String PARENT_GROUP_ID_XPATH = "/project/parent/groupId"; private static final String PARENT_XPATH = "/project/parent"; private static final String PLUGIN_REPOSITORY_XPATH = "/project/pluginRepositories/pluginRepository"; private static final String PLUGIN_IN_DEPENDENCY_MANAGEMENT_XPATH = "/project/build/pluginManagement/plugins/plugin"; private static final String PLUGIN_XPATH = "/project/build/plugins/plugin"; private static final String PROPERTY_XPATH = "/project/properties/*"; private static final String REPOSITORY_XPATH = "/project/repositories/repository"; private static final String RESOURCE_XPATH = "/project/build/resources/resource"; private static final String SOURCE_DIRECTORY_XPATH = "/project/build/sourceDirectory"; private static final String TEST_SOURCE_DIRECTORY_XPATH = "/project/build/testSourceDirectory"; private static final String VERSION_XPATH = "/project/version"; @Reference PackagingProviderRegistry packagingProviderRegistry; /** * Returns the groupId defined in the given POM * * @param root the POM's root element (required) * @return a non-blank groupId */ private String getGroupId(final Element root) { final String projectGroupId = XmlUtils.getTextContent(GROUP_ID_XPATH, root); if (StringUtils.isNotBlank(projectGroupId)) { return projectGroupId; } // Fall back to a group ID assumed to be the same as any possible // <parent> (ROO-1193) return XmlUtils.getTextContent(PARENT_GROUP_ID_XPATH, root); } public Pom getInstance(final Element root, final String pomPath, final String moduleName) { Validate.notBlank(pomPath, "POM's canonical path is required"); final String artifactId = XmlUtils.getTextContent(ARTIFACT_ID_XPATH, root); final String groupId = getGroupId(root); final String name = XmlUtils.getTextContent(NAME_XPATH, root); final String packaging = XmlUtils.getTextContent(PACKAGING_XPATH, root, DEFAULT_PACKAGING); String version = XmlUtils.getTextContent(VERSION_XPATH, root); final String sourceDirectory = XmlUtils.getTextContent(SOURCE_DIRECTORY_XPATH, root); final String testSourceDirectory = XmlUtils.getTextContent(TEST_SOURCE_DIRECTORY_XPATH, root); final List<Dependency> dependenciesInDependencyManagement = parseElements(Dependency.class, DEPENDENCY_IN_DEPENDENCY_MANAGEMENT_XPATH, root); final List<Dependency> dependencies = parseElements(Dependency.class, DEPENDENCY_XPATH, root); final List<Filter> filters = parseElements(Filter.class, FILTER_XPATH, root); final List<Module> modules = getModules(root, pomPath, packaging); final List<Plugin> pluginsInDependencyManagement = parseElements(Plugin.class, PLUGIN_IN_DEPENDENCY_MANAGEMENT_XPATH, root); final List<Plugin> plugins = parseElements(Plugin.class, PLUGIN_XPATH, root); final List<Property> pomProperties = parseElements(Property.class, PROPERTY_XPATH, root); final List<Repository> pluginRepositories = parseElements(Repository.class, PLUGIN_REPOSITORY_XPATH, root); final List<Repository> repositories = parseElements(Repository.class, REPOSITORY_XPATH, root); final List<Resource> resources = parseElements(Resource.class, RESOURCE_XPATH, root); final String projectParentVersion = XmlUtils.getTextContent("/project/parent/version", root); final Parent parent = getParent(pomPath, root); if (version == null) { version = projectParentVersion; } final Collection<Path> paths = getPaths(root, packaging); return new Pom(groupId, artifactId, version, packaging, dependenciesInDependencyManagement, dependencies, parent, modules, pomProperties, name, repositories, pluginRepositories, sourceDirectory, testSourceDirectory, filters, pluginsInDependencyManagement, plugins, resources, pomPath, moduleName, paths); } private List<Module> getModules(final Element root, final String pomPath, final String packaging) { if (!"pom".equalsIgnoreCase(packaging)) { return null; } final List<Module> modules = new ArrayList<Module>(); for (final Element module : XmlUtils.findElements(MODULE_XPATH, root)) { final String moduleName = module.getTextContent(); if (StringUtils.isNotBlank(moduleName)) { final String modulePath = resolveRelativePath(pomPath, moduleName); modules.add(new Module(moduleName, modulePath)); } } return modules; } private Parent getParent(final String pomPath, final Element root) { final Element parentElement = XmlUtils.findFirstElement(PARENT_XPATH, root); if (parentElement == null) { return null; } final String relativePath = XmlUtils.getTextContent("/relativePath", parentElement, DEFAULT_RELATIVE_PATH); final String parentPomPath = resolveRelativePath(pomPath, relativePath); return new ParentBuilder(parentElement, parentPomPath).build(); } private Collection<Path> getPaths(final Element root, final String packaging) { final String packagingProviderId = XmlUtils.getTextContent(PACKAGING_PROVIDER_PROPERTY_XPATH, root, packaging); final PackagingProvider packagingProvider = packagingProviderRegistry.getPackagingProvider(packagingProviderId); Validate.notNull(packagingProvider, "No PackagingProvider found with the ID '%s'", packagingProviderId); return packagingProvider.getPaths(); } /** * Parses any elements matching the given XPath expression into instances of * the given type. * * @param <T> the type of object to parse * @param type the type of object to parse; must have a constructor that * accepts an {@link Element} as its sole argument * @param xPath the XPath expression to apply (required) * @param root the root of the XML document being searched (required) * @return a non-<code>null</code> list */ private <T> List<T> parseElements(final Class<T> type, final String xPath, final Element root) { final List<T> results = new ArrayList<T>(); for (final Element element : XmlUtils.findElements(xPath, root)) { try { results.add(type.getConstructor(Element.class).newInstance(element)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { throw new RuntimeException(e); } } return results; } private String resolveRelativePath(String relativeTo, final String relativePath) { relativeTo = StringUtils.removeEnd(relativeTo, File.separator); while (new File(relativeTo).isFile()) { relativeTo = relativeTo.substring(0, relativeTo.lastIndexOf(File.separator)); } final String[] relativePathSegments = relativePath.split(FileUtils.getFileSeparatorAsRegex()); int backCount = 0; for (final String relativePathSegment : relativePathSegments) { if (relativePathSegment.equals("..")) { backCount++; } else { break; } } final StringBuilder sb = new StringBuilder(); for (int i = backCount; i < relativePathSegments.length; i++) { sb.append(relativePathSegments[i]); sb.append(File.separator); } while (backCount > 0) { relativeTo = relativeTo.substring(0, relativeTo.lastIndexOf(File.separatorChar)); backCount--; } String path = relativeTo + File.separator + sb.toString(); if (new File(path).isDirectory()) { path = path + "pom.xml"; } return path; } }