/*******************************************************************************
* Copyright (c) 2010 Neil Bartlett.
* 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
*
* Contributors:
* Neil Bartlett - initial API and implementation
*******************************************************************************/
package bndtools.editor.exports;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
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.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.ide.ResourceUtil;
import aQute.bnd.build.Project;
import aQute.bnd.build.model.BndEditModel;
import aQute.bnd.build.model.clauses.ExportedPackage;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Constants;
import bndtools.Plugin;
import bndtools.central.Central;
import bndtools.editor.contents.PackageInfoDialog;
import bndtools.editor.contents.PackageInfoStyle;
import bndtools.editor.contents.PackageInfoDialog.FileVersionTuple;
import bndtools.editor.pkgpatterns.PkgPatternsListPart;
import bndtools.internal.pkgselection.IPackageFilter;
import bndtools.internal.pkgselection.JavaSearchScopePackageLister;
import bndtools.internal.pkgselection.PackageSelectionDialog;
import bndtools.preferences.BndPreferences;
public class ExportPatternsListPart extends PkgPatternsListPart<ExportedPackage> {
private static final ILogger logger = Logger.getLogger(ExportPatternsListPart.class);
public ExportPatternsListPart(Composite parent, FormToolkit toolkit, int style) {
super(parent, toolkit, style, Constants.EXPORT_PACKAGE, "Export Packages", new ExportedPackageLabelProvider());
}
@Override
protected Collection<ExportedPackage> generateClauses() {
return selectPackagesToAdd();
}
protected List<ExportedPackage> selectPackagesToAdd() {
List<ExportedPackage> added = null;
final IPackageFilter filter = new IPackageFilter() {
@Override
public boolean select(String packageName) {
if (packageName.equals("java") || packageName.startsWith("java."))
return false;
// TODO: check already included patterns
return true;
}
};
IFormPage page = (IFormPage) getManagedForm().getContainer();
IWorkbenchWindow window = page.getEditorSite().getWorkbenchWindow();
// Prepare the package lister from the Java project
IProject project = ResourceUtil.getResource(page.getEditorInput()).getProject();
IJavaProject javaProject = JavaCore.create(project);
IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(new IJavaElement[] {
javaProject
});
JavaSearchScopePackageLister packageLister = new JavaSearchScopePackageLister(searchScope, window);
// Create and open the dialog
PackageSelectionDialog dialog = new PackageSelectionDialog(window.getShell(), packageLister, filter, "Select new packages to export from the bundle.");
dialog.setSourceOnly(true);
dialog.setMultipleSelection(true);
if (dialog.open() == Window.OK) {
Object[] results = dialog.getResult();
added = new LinkedList<ExportedPackage>();
// Select the results
for (Object result : results) {
String newPackageName = (String) result;
ExportedPackage newPackage = new ExportedPackage(newPackageName, new Attrs());
added.add(newPackage);
}
}
return added;
}
@Override
protected void doAddClauses(Collection< ? extends ExportedPackage> pkgs, int index, boolean select) {
List<FileVersionTuple> missingPkgInfoDirs;
try {
missingPkgInfoDirs = new ArrayList<FileVersionTuple>(findSourcePackagesWithoutPackageInfo(pkgs));
} catch (Exception e) {
ErrorDialog.openError(getManagedForm().getForm().getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error finding source package for exported 1packages.", e));
missingPkgInfoDirs = Collections.emptyList();
}
List<FileVersionTuple> generatePkgInfoDirs = new ArrayList<FileVersionTuple>(missingPkgInfoDirs.size());
BndPreferences prefs = new BndPreferences();
boolean noAskPackageInfo = prefs.getNoAskPackageInfo();
if (noAskPackageInfo || missingPkgInfoDirs.isEmpty()) {
generatePkgInfoDirs.addAll(missingPkgInfoDirs);
} else {
PackageInfoDialog dlg = new PackageInfoDialog(getSection().getShell(), missingPkgInfoDirs);
if (dlg.open() == Window.CANCEL)
return;
prefs.setNoAskPackageInfo(dlg.isDontAsk());
generatePkgInfoDirs.addAll(dlg.getSelectedPackageDirs());
}
try {
generatePackageInfos(generatePkgInfoDirs);
} catch (CoreException e) {
ErrorDialog.openError(getManagedForm().getForm().getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error generated packageinfo files.", e));
}
// Actually add the new exports
super.doAddClauses(pkgs, index, select);
}
private Collection<FileVersionTuple> findSourcePackagesWithoutPackageInfo(Collection< ? extends ExportedPackage> pkgs) throws Exception {
Map<String,FileVersionTuple> result = new HashMap<String,FileVersionTuple>();
Project project = getProject();
if (project != null) {
Collection<File> sourceDirs = project.getSourcePath();
for (File sourceDir : sourceDirs) {
for (ExportedPackage pkg : pkgs) {
if (!result.containsKey(pkg.getName())) {
File pkgDir = new File(sourceDir, pkg.getName().replace('.', '/'));
if (pkgDir.isDirectory()) {
PackageInfoStyle existingPkgInfo = PackageInfoStyle.findExisting(pkgDir);
if (existingPkgInfo == null)
result.put(pkg.getName(), new FileVersionTuple(pkg.getName(), pkgDir));
}
}
}
}
}
return result.values();
}
private static void generatePackageInfos(final Collection< ? extends FileVersionTuple> pkgs) throws CoreException {
final IWorkspaceRunnable wsOperation = new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
SubMonitor progress = SubMonitor.convert(monitor, pkgs.size());
MultiStatus status = new MultiStatus(Plugin.PLUGIN_ID, 0, "Errors occurred while creating packageinfo files.", null);
for (FileVersionTuple pkg : pkgs) {
IContainer[] locations = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(pkg.getFile().toURI());
if (locations != null && locations.length > 0) {
IContainer container = locations[0];
PackageInfoStyle packageInfoStyle = PackageInfoStyle.calculatePackageInfoStyle(container.getProject());
IFile pkgInfoFile = container.getFile(new Path(packageInfoStyle.getFileName()));
try {
String formattedPackageInfo = packageInfoStyle.format(pkg.getVersion(), pkg.getName());
ByteArrayInputStream input = new ByteArrayInputStream(formattedPackageInfo.getBytes("UTF-8"));
if (pkgInfoFile.exists())
pkgInfoFile.setContents(input, false, true, progress.newChild(1, 0));
else
pkgInfoFile.create(input, false, progress.newChild(1, 0));
} catch (CoreException e) {
status.add(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error creating file " + pkgInfoFile.getFullPath(), e));
} catch (UnsupportedEncodingException e) {
/* just ignore, should never happen */
}
}
}
if (!status.isOK())
throw new CoreException(status);
}
};
IRunnableWithProgress uiOperation = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
ResourcesPlugin.getWorkspace().run(wsOperation, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().run(true, true, uiOperation);
} catch (InvocationTargetException e) {
throw (CoreException) e.getTargetException();
} catch (InterruptedException e) {
// ignore
}
}
@Override
protected ExportedPackage newHeaderClause(String text) {
return new ExportedPackage(text, new Attrs());
}
@Override
protected List<ExportedPackage> loadFromModel(BndEditModel model) {
return model.getExportedPackages();
}
@Override
protected void saveToModel(BndEditModel model, List< ? extends ExportedPackage> clauses) {
model.setExportedPackages(clauses);
}
Project getProject() {
Project project = null;
try {
BndEditModel model = (BndEditModel) getManagedForm().getInput();
File bndFile = model.getBndResource();
IPath path = Central.toPath(bndFile);
IFile resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
project = Central.getProject(resource.getProject());
} catch (Exception e) {
logger.logError("Error getting project from editor model", e);
}
return project;
}
}