package org.bndtools.core.resolve.ui;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.bndtools.core.resolve.ResolutionResult;
import org.bndtools.utils.resources.ResourceUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.wizard.Wizard;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.resource.Capability;
import org.osgi.resource.Resource;
import aQute.bnd.build.Project;
import aQute.bnd.build.model.BndEditModel;
import aQute.bnd.build.model.clauses.VersionedClause;
import aQute.bnd.header.Attrs;
import aQute.bnd.version.VersionRange;
import aQute.lib.io.IO;
import bndtools.BndConstants;
public class ResolutionWizard extends Wizard {
private static final String VERSION_SNAPSHOT = "snapshot";
private static final String CAPABILITY_WORKSPACE = "bndtools.workspace";
private static final String RESOLVED_PATHS_EXTENSION = ".resolved";
private final ILogger logger = Logger.getLogger(ResolutionWizard.class);
private final ResolutionResultsWizardPage resultsPage;
private final Comparator<Entry<String,String>> clauseAttributeSorter = new Comparator<Map.Entry<String,String>>() {
@Override
public int compare(Entry<String,String> e1, Entry<String,String> e2) {
// Reverse lexical ordering on keys
return e2.getKey().compareTo(e1.getKey());
}
};
private final BndEditModel model;
private final IFile file;
public ResolutionWizard(BndEditModel model, IFile file, ResolutionResult result) {
this.model = model;
this.file = file;
resultsPage = new ResolutionResultsWizardPage(model);
resultsPage.setResult(result);
setWindowTitle("Resolve");
setNeedsProgressMonitor(true);
addPage(resultsPage);
}
@Override
public boolean performFinish() {
Collection<Resource> resources;
ResolutionResult result = resultsPage.getResult();
if (result != null && result.getOutcome() == ResolutionResult.Outcome.Resolved)
resources = result.getResourceWirings().keySet();
else
resources = Collections.emptyList();
// Open stream for physical paths list in target dir
PrintStream pathsStream = null;
try {
File targetDir;
Project bndProject = model.getProject();
targetDir = bndProject.getTargetDir();
if (targetDir == null)
targetDir = file.getLocation().toFile().getParentFile();
if (!targetDir.exists() && !targetDir.mkdirs()) {
throw new IOException("Could not create target directory " + targetDir);
}
File pathsFile = new File(targetDir, file.getName() + RESOLVED_PATHS_EXTENSION);
pathsStream = new PrintStream(pathsFile, "UTF-8");
} catch (Exception e) {
logger.logError("Unable to write resolved path list in target directory for project " + file.getProject().getName(), e);
}
// Generate -runbundles and path list
try {
List<VersionedClause> runBundles = new ArrayList<VersionedClause>(resources.size());
for (Resource resource : resources) {
VersionedClause runBundle = resourceToRunBundle(resource);
//[cs] Skip dups
if (runBundles.contains(runBundle)) {
continue;
}
runBundles.add(runBundle);
if (pathsStream != null) {
VersionedClause runBundleWithUri = runBundle.clone();
URI uri = ResourceUtils.getURI(ResourceUtils.getContentCapability(resource));
runBundleWithUri.getAttribs().put(BndConstants.RESOLUTION_URI_ATTRIBUTE, uri.toString());
StringBuilder builder = new StringBuilder();
runBundleWithUri.formatTo(builder, clauseAttributeSorter);
pathsStream.println(builder.toString());
}
}
Collections.sort(runBundles, new Comparator<VersionedClause>() {
@Override
public int compare(VersionedClause vc1, VersionedClause vc2) {
int diff = vc1.getName().compareTo(vc2.getName());
if (diff != 0)
return diff;
String r1 = vc1.getVersionRange();
if (r1 == null)
r1 = "";
String r2 = vc2.getVersionRange();
if (r2 == null)
r2 = "";
return r1.compareTo(r2);
}
});
// Do not change the order of existing runbundles because they migh have been ordered manually
List<VersionedClause> diffAddBundles = new ArrayList<>(runBundles);
List<VersionedClause> oldRunBundles = model.getRunBundles();
if (oldRunBundles == null)
oldRunBundles = Collections.emptyList();
else
diffAddBundles.removeAll(oldRunBundles);
List<VersionedClause> diffRemvedBundles = new ArrayList<>(oldRunBundles);
diffRemvedBundles.removeAll(runBundles);
List<VersionedClause> updatedRunBundles = new ArrayList<>(oldRunBundles);
updatedRunBundles.addAll(diffAddBundles);
updatedRunBundles.removeAll(diffRemvedBundles);
// do not use getRunBundles().addAll, because it will not reflect in UI or File
model.setRunBundles(updatedRunBundles);
} finally {
if (pathsStream != null) {
IO.close(pathsStream);
}
}
return true;
}
private static VersionedClause resourceToRunBundle(Resource resource) {
Capability idCap = ResourceUtils.getIdentityCapability(resource);
String identity = ResourceUtils.getIdentity(idCap);
// Map version range string, using "latest" for any workspace resources
Attrs attribs = new Attrs();
String versionRangeStr;
if (isWorkspace(resource)) {
versionRangeStr = VERSION_SNAPSHOT;
} else {
Version version = ResourceUtils.getVersion(idCap);
VersionRange versionRange = createVersionRange(version);
versionRangeStr = versionRange.toString();
}
attribs.put(Constants.VERSION_ATTRIBUTE, versionRangeStr);
return new VersionedClause(identity, attribs);
}
private static boolean isWorkspace(Resource resource) {
List<Capability> workspaceCaps = resource.getCapabilities(CAPABILITY_WORKSPACE);
return workspaceCaps != null && !workspaceCaps.isEmpty();
}
private static VersionRange createVersionRange(Version version) {
Version base = new Version(version.getMajor(), version.getMinor(), version.getMicro());
Version next = new Version(version.getMajor(), version.getMinor(), version.getMicro() + 1);
return new VersionRange(String.format("[%s,%s)", base, next));
}
}