/**
* Copyright (c) 2012 to original author or authors
* 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
*/
package org.sonatype.maven.polyglot.groovy.builder;
import groovy.lang.Closure;
import groovy.util.Factory;
import groovy.util.FactoryBuilderSupport;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.model.Activation;
import org.apache.maven.model.Contributor;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DeploymentRepository;
import org.apache.maven.model.Developer;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Extension;
import org.apache.maven.model.License;
import org.apache.maven.model.MailingList;
import org.apache.maven.model.Model;
import org.apache.maven.model.Notifier;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportSet;
import org.apache.maven.model.Reporting;
import org.apache.maven.model.Repository;
import org.apache.maven.model.Resource;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.sonatype.maven.polyglot.execute.ExecuteManager;
import org.sonatype.maven.polyglot.execute.ExecuteTask;
import org.sonatype.maven.polyglot.groovy.builder.factory.ChildFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.DependencyFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExcludesFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExclusionFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExclusionsFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExecuteFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExecutionFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ExtensionsFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.GoalsFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.IncludesFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ListFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ModelFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ModulesFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.NamedFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ObjectFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ParentFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.PluginFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.PropertiesFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ReportSetsFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ReportingFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.ReportsFactory;
import org.sonatype.maven.polyglot.groovy.builder.factory.StringFactory;
/**
* Builds Maven {@link Model} instances.
*
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
*
* @since 0.7
*/
@Component(role=ModelBuilder.class)
public class ModelBuilder
extends FactoryBuilderSupport
implements Initializable
{
@Requirement
protected Logger log;
private final Set<String> factoryNames = new HashSet<String>();
private final Set<Class> factoryTypes = new HashSet<Class>();
private final List<ExecuteTask> tasks = new ArrayList<ExecuteTask>();
@Requirement
private ExecuteManager executeManager;
public void initialize() throws InitializationException {
registerFactories();
}
@Override
protected void setClosureDelegate(final Closure c, final Object o) {
c.setDelegate(this);
c.setResolveStrategy(Closure.DELEGATE_FIRST);
}
@Override
public void setVariable(final String name, final Object value) {
InvokerHelper.setProperty(getCurrent(), name, value);
}
public ExecuteManager getExecuteManager() {
return executeManager;
}
public List<ExecuteTask> getTasks() {
return tasks;
}
public void registerFactories() {
registerStringFactory("module");
registerStringFactory("filter");
registerStringFactory("include");
registerStringFactory("exclude");
registerStringFactory("goal");
registerStringFactory("role");
registerStringFactory("otherArchive");
registerStringFactory("activeByDefault");
registerStringFactory("report");
registerFactory(new PluginFactory());
registerFactoriesFor(Plugin.class);
registerFactory(new ModulesFactory());
registerFactory(new ExclusionsFactory());
registerFactory(new ExtensionsFactory());
registerFactory(new IncludesFactory());
registerFactory(new ExcludesFactory());
registerFactory(new GoalsFactory());
registerFactory(new ExecuteFactory());
registerFactory(new ReportSetsFactory());
registerFactory(new ReportsFactory());
registerFactory(new ReportingFactory());
registerFactoriesFor(Reporting.class);
registerFactory(new ExecutionFactory());
registerFactoriesFor(PluginExecution.class);
registerFactory(new ModelFactory());
registerFactoriesFor(Model.class);
registerChildFactory("dependency", Dependency.class);
registerChildFactory("exclusion", Exclusion.class);
registerChildFactory("extension", Extension.class);
registerChildFactory("resource", Resource.class);
registerChildFactory("testResource", Resource.class);
registerChildFactory("notifier", Notifier.class);
registerChildFactory("contributor", Contributor.class);
registerChildFactory("developer", Developer.class);
registerChildFactory("license", License.class);
registerChildFactory("mailingList", MailingList.class);
registerChildFactory("profile", Profile.class);
registerChildFactory("repository", DeploymentRepository.class);
registerChildFactory("pluginRepository", Repository.class);
registerChildFactory("reportSet", ReportSet.class);
registerChildFactory("activation", Activation.class);
}
@Override
public void registerBeanFactory(final String name, final Class type) {
super.registerBeanFactory(name, type);
registerFactoriesFor(type);
}
@Override
public void registerFactory(final String name, final String groupName, final Factory factory) {
if (log.isDebugEnabled()) {
log.debug("Registering factory: " + name + ", factory: " + factory);
if (factoryNames.contains(name)) {
log.warn("Duplicate factory: " + name);
}
}
factoryNames.add(name);
super.registerFactory(name, groupName, factory);
}
private void registerFactory(final NamedFactory factory) {
assert factory != null;
registerFactory(factory.getName(), null, factory);
}
private void registerChildFactory(final String name, final Class type) {
registerFactory(createChildFactory(name, type));
registerFactoriesFor(type);
}
private NamedFactory createChildFactory(final String name, final Class type) {
assert name != null;
assert type != null;
if (type == Parent.class) {
return new ParentFactory();
}
if (type == Dependency.class) {
return new DependencyFactory();
}
if (type == Exclusion.class) {
return new ExclusionFactory();
}
return new ChildFactory(name, type);
}
private void registerStringFactory(final String name) {
registerFactory(new StringFactory(name));
}
private void registerListFactory(final String name) {
registerFactory(new ListFactory(name));
}
private void registerPropertiesFactory(final String name) {
registerFactory(new PropertiesFactory(name));
}
private void registerObjectFactory(final String name) {
registerFactory(new ObjectFactory(name));
}
private void registerFactoriesFor(final Class type) {
assert type != null;
if (factoryTypes.contains(type)) {
return;
}
factoryTypes.add(type);
if (log.isDebugEnabled()) {
log.debug("Registering factories for type: " + type);
}
Method[] methods = type.getMethods();
for (Method method : methods) {
if (isSetter(method) ){
String name = propertyNameOf(method);
if (factoryNames.contains(name)) {
continue;
}
Class param = method.getParameterTypes()[0];
if (param == String.class) {
registerStringFactory(name);
}
else if (param == List.class) {
registerListFactory(name);
}
else if (param == Properties.class) {
registerPropertiesFactory(name);
}
else if (param == Object.class) {
registerObjectFactory(name);
}
else if (param.getName().startsWith("org.apache.maven.model.")) {
registerChildFactory(name, param);
}
else {
// Skip setters with unsupported types (model will use string versions)
if (log.isDebugEnabled()) {
log.debug("Skipping setter with unsupported type: " + method);
}
}
}
}
}
private boolean isSetter(final Method method) {
assert method != null;
if (!method.getName().startsWith("set")) {
return false;
}
if (method.getParameterTypes().length > 1) {
return false;
}
if (method.getReturnType() != Void.TYPE) {
return false;
}
int m = method.getModifiers();
if (!Modifier.isPublic(m) || Modifier.isStatic(m)) {
return false;
}
return true;
}
private String propertyNameOf(final Method method) {
assert method != null;
String name = method.getName();
name = name.substring(3, name.length());
return new StringBuffer(name.length())
.append(Character.toLowerCase(name.charAt(0)))
.append(name.substring(1))
.toString();
}
public Object findInContext(final String key) {
for (Map<String,Object> ctx : getContexts()) {
if (ctx.containsKey(key)) {
return ctx.get(key);
}
}
return null;
}
// BEGIN: Kludgy Workaround
// Explanation: When I translated the slf4j pom.xml to pom.groovy, I ended up with this:
// plugin {
// artifactId 'maven-project-info-reports-plugin'
// reportSets {
// reportSet
// }
// }
// The ModelBuilder saw this, and assumed that it was trying to populate the reportSet method. Note
// that this shows up because the slf4j pom.xml includes
// a <reportSets><reportSet><report/></reportSet></reportSets>
private ReportSet reportSet;
public ReportSet getReportSet() {
return reportSet;
}
public void setReportSet(ReportSet reportSet) {
this.reportSet = reportSet;
}
// END: Kludgy Workaround
}