package bndtools.wizards.bndfile; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bndtools.api.BndtoolsConstants; import org.bndtools.utils.workspace.FileUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Shell; import aQute.bnd.build.Project; import aQute.bnd.build.model.BndEditModel; import aQute.bnd.osgi.Constants; import aQute.bnd.properties.Document; import bndtools.Plugin; import bndtools.central.Central; public class EnableSubBundlesOperation implements IWorkspaceRunnable { private static final Set<String> BUNDLE_SPECIFIC_HEADERS; static { BUNDLE_SPECIFIC_HEADERS = new HashSet<String>(Arrays.asList(Constants.BUNDLE_SPECIFIC_HEADERS)); BUNDLE_SPECIFIC_HEADERS.add(Constants.SERVICE_COMPONENT); BUNDLE_SPECIFIC_HEADERS.add(Constants.BUNDLE_VERSION); } private static final Set<String> PROJECT_ONLY_HEADERS = new HashSet<String>(Arrays.asList(new String[] { "-buildpath", "-runbundles", "-runsystempackages", "-runpath", "-runvm", "-runtrace", "-runframework", "-runfw", "-sub", "-debug" })); private final Shell parentShell; private final IWorkspace workspace; private final IPath containerPath; private InputStream newBundleInputStream = null; public EnableSubBundlesOperation(Shell parentShell, IWorkspace workspace, IPath containerPath) { this.parentShell = parentShell; this.workspace = workspace; this.containerPath = containerPath; } @Override public void run(final IProgressMonitor monitor) throws CoreException { IResource container = workspace.getRoot().findMember(containerPath); if (container == null || !container.exists()) throw newCoreException("Container path does not exist", null); // Create new project model BndEditModel newBundleModel; try { newBundleModel = new BndEditModel(Central.getWorkspace()); } catch (Exception e) { System.err.println("Unable to create BndEditModel with Workspace, defaulting to without Workspace"); newBundleModel = new BndEditModel(); } // Load project file and model IProject project = container.getProject(); IFile projectFile = project.getFile(Project.BNDFILE); BndEditModel projectModel; final Document projectDocument; try { if (projectFile.exists()) { byte[] bytes = FileUtils.readFully(projectFile.getContents()); projectDocument = new Document(new String(bytes, projectFile.getCharset())); } else { projectDocument = new Document(""); } try { projectModel = new BndEditModel(Central.getWorkspace()); } catch (Exception e) { System.err.println("Unable to create BndEditModel with Workspace, defaulting to without Workspace"); projectModel = new BndEditModel(); } projectModel.loadFrom(projectDocument); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, e.getMessage(), e)); } // Sub-bundles are meaningless outside of Bndtools if (project.hasNature(BndtoolsConstants.NATURE_ID)) { // Check if we need to enable sub-bundles on the project file boolean enableSubs; List<String> subBndFiles = projectModel.getSubBndFiles(); List<String> availableHeaders = calculateProjectOnlyHeaders(projectModel.getAllPropertyNames()); Collection<String> bundleSpecificHeaders = calculateBundleSpecificHeaders(availableHeaders); if (subBndFiles == null || subBndFiles.isEmpty()) { final EnableSubBundlesDialog subBundlesDialog = new EnableSubBundlesDialog(parentShell, availableHeaders, bundleSpecificHeaders); if (subBundlesDialog.open() != Window.OK) { monitor.setCanceled(true); return; } enableSubs = subBundlesDialog.isEnableSubBundles(); bundleSpecificHeaders = subBundlesDialog.getSelectedProperties(); } else { enableSubs = false; } // Enable subs and copy entries from project model to new bundle model if (enableSubs) { projectModel.setSubBndFiles(Arrays.asList(new String[] { "*.bnd" })); for (String propertyName : bundleSpecificHeaders) { Object value = projectModel.genericGet(propertyName); projectModel.genericSet(propertyName, null); newBundleModel.genericSet(propertyName, value); } // Save the project model projectModel.saveChangesTo(projectDocument); FileUtils.writeFully(projectDocument.get(), projectFile, false); } } // Generate the new bundle model Document newBundleDocument = new Document(""); newBundleModel.saveChangesTo(newBundleDocument); try { newBundleInputStream = new ByteArrayInputStream(newBundleDocument.get().getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { newBundleInputStream = null; } } private static List<String> calculateProjectOnlyHeaders(List<String> allPropertyNames) { List<String> result = new ArrayList<String>(allPropertyNames.size()); for (String propertyName : allPropertyNames) { if (!PROJECT_ONLY_HEADERS.contains(propertyName)) result.add(propertyName); } return result; } public static List<String> calculateBundleSpecificHeaders(Collection<String> propertyNames) { List<String> result = new ArrayList<String>(propertyNames.size()); for (String propertyName : propertyNames) { if (BUNDLE_SPECIFIC_HEADERS.contains(propertyName)) result.add(propertyName); } return result; } public static void moveBundleContentProperties(BndEditModel sourceModel, BndEditModel destModel, List<String> properties) { for (String property : properties) { Object value = sourceModel.genericGet(property); destModel.genericSet(property, value); sourceModel.genericSet(property, null); } } private static CoreException newCoreException(String message, Throwable cause) { return new CoreException(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, message, cause)); } public InputStream getNewBundleInputStream() { return newBundleInputStream; } }