/*
* Copyright 2014 Bernd Vogt and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sourcepit.b2.internal.maven;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.sourcepit.b2.maven.core.B2MavenBridge;
import org.sourcepit.b2.model.interpolation.internal.module.B2MetadataUtils;
import org.sourcepit.b2.model.module.AbstractModule;
import org.sourcepit.b2.model.module.FeatureProject;
import org.sourcepit.b2.model.module.FeaturesFacet;
import org.sourcepit.common.maven.model.MavenArtifact;
import com.google.common.base.Optional;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.SetMultimap;
@Named
public class ModelContextAdapterFactory {
private final ModuleArtifactResolver artifactResolver;
@Inject
public ModelContextAdapterFactory(ModuleArtifactResolver artifactResolver) {
this.artifactResolver = artifactResolver;
}
public ModelContext adapt(MavenSession session, MavenProject project) {
synchronized (project) {
ModelContext context = (ModelContext) project.getContextValue(ModelContext.class.getName());
if (context == null) {
context = createModuleModelContext(session, project);
project.setContextValue(ModelContext.class.getName(), context);
}
return context;
}
}
public static ModelContext get(MavenProject project) {
synchronized (project) {
return (ModelContext) project.getContextValue(ModelContext.class.getName());
}
}
private ModelContext createModuleModelContext(MavenSession session, MavenProject project) {
final ResourceSet resourceSet = createResourceSet();
final SetMultimap<URI, String> scopeTest = LinkedHashMultimap.create();
initURIMapping(resourceSet, scopeTest, session, project, "test");
final SetMultimap<URI, String> scopeCompile = LinkedHashMultimap.create();
initURIMapping(resourceSet, scopeCompile, session, project, "compile");
initURIMapping(resourceSet, session, project);
final URI artifactURI = B2MavenBridge.toArtifactURI(project, "module", null);
final URI fileURI = URI.createFileURI(pathOfMetaDataFile(project.getBasedir(), "b2.module"));
resourceSet.getURIConverter().getURIMap().put(artifactURI, fileURI);
return new ModelContext(resourceSet, artifactURI.appendFragment("/"), resolve(resourceSet, scopeCompile),
resolve(resourceSet, scopeTest));
}
private void initURIMapping(ResourceSet resourceSet, MavenSession session, MavenProject currentProject) {
final Map<URI, URI> uriMap = resourceSet.getURIConverter().getURIMap();
final List<MavenProject> projects = session.getProjects();
for (int i = 0; i < projects.indexOf(currentProject); i++) {
final ModelContext foreignModelContext = ModelContextAdapterFactory.get(projects.get(i));
if (foreignModelContext != null) {
final Map<URI, URI> foreignUriMap = foreignModelContext.getResourceSet().getURIConverter().getURIMap();
for (URI uri : foreignUriMap.keySet()) {
if (!uriMap.containsKey(uri)) {
uriMap.put(uri, foreignUriMap.get(uri));
}
}
}
}
}
private static SetMultimap<AbstractModule, FeatureProject> resolve(ResourceSet resourceSet,
SetMultimap<URI, String> unresolved) {
final SetMultimap<AbstractModule, FeatureProject> resolved = LinkedHashMultimap.create(unresolved.size(), 4);
for (Entry<URI, Collection<String>> entry : unresolved.asMap().entrySet()) {
final URI uri = entry.getKey();
final AbstractModule module = (AbstractModule) resourceSet.getResource(uri, true).getContents().get(0);
for (String classifier : entry.getValue()) {
final Optional<FeatureProject> result = findAssemblyFeatureForClassifier(module, classifier);
if (!result.isPresent()) {
throw new IllegalStateException("Cannot determine assembly feature for classifier '" + classifier + "'");
}
resolved.get(module).add(result.get());
}
}
return resolved;
}
private static Optional<FeatureProject> findAssemblyFeatureForClassifier(final AbstractModule module,
String classifier) {
for (FeaturesFacet featuresFacet : module.getFacets(FeaturesFacet.class)) {
for (FeatureProject featureProject : featuresFacet.getProjects()) {
final int idx = B2MetadataUtils.getAssemblyClassifiers(featureProject).indexOf(classifier);
if (idx > -1) {
return Optional.of(featureProject);
}
}
}
return Optional.absent();
}
// TODO use interpolation layout
private static String pathOfMetaDataFile(File directory, String name) {
final StringBuilder sb = new StringBuilder();
final String modulePath = directory.getAbsolutePath();
if (modulePath.length() != 0) {
sb.append(modulePath);
sb.append(File.separatorChar);
}
sb.append(".b2");
sb.append(File.separatorChar);
sb.append(name);
return sb.toString();
}
private static ResourceSet createResourceSet() {
final ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getProtocolToFactoryMap().put("gav", new XMIResourceFactoryImpl());
resourceSet.getResourceFactoryRegistry().getProtocolToFactoryMap().put("file", new XMIResourceFactoryImpl());
return resourceSet;
}
private void initURIMapping(ResourceSet resourceSet, SetMultimap<URI, String> artifactURIs, MavenSession session,
MavenProject project, String scope) {
// resolve module artifacts
final SetMultimap<MavenArtifact, String> moduleArtifactToClassifiers = artifactResolver.resolve(session, project,
scope);
// create artifact to file uri mapping
for (Entry<MavenArtifact, Collection<String>> entry : moduleArtifactToClassifiers.asMap().entrySet()) {
final MavenArtifact artifact = entry.getKey();
final URI artifactURI = toArtifactURI(artifact, null);
final URI fileURI = URI.createFileURI(artifact.getFile().getAbsolutePath());
artifactURIs.get(artifactURI).addAll(entry.getValue());
resourceSet.getURIConverter().getURIMap().put(artifactURI, fileURI);
}
}
private static URI toArtifactURI(MavenArtifact artifact, String classifier) {
final StringBuilder sb = new StringBuilder();
sb.append(artifact.getGroupId());
sb.append("/");
sb.append(artifact.getArtifactId());
sb.append("/");
sb.append(artifact.getType());
if (classifier != null && classifier.length() > 0) {
sb.append("/");
sb.append(classifier);
}
sb.append("/");
sb.append(artifact.getVersion());
return URI.createURI("gav:/" + sb.toString());
}
}