diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/PluginRegistry.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/PluginRegistry.java index 85cda7d26a..0b442f897f 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/PluginRegistry.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/PluginRegistry.java @@ -407,13 +407,15 @@ private static IPluginModelBase[] findModels(String id) { * @since 3.7 */ public static IBuildModel createBuildModel(IPluginModelBase model) throws CoreException { - IProject project = model.getUnderlyingResource().getProject(); - if (project != null) { - IFile buildFile = PDEProject.getBuildProperties(project); - if (buildFile.exists()) { - IBuildModel buildModel = new WorkspaceBuildModel(buildFile); - buildModel.load(); - return buildModel; + if (model != null) { + IProject project = model.getUnderlyingResource().getProject(); + if (project != null) { + IFile buildFile = PDEProject.getBuildProperties(project); + if (buildFile.exists()) { + IBuildModel buildModel = new WorkspaceBuildModel(buildFile); + buildModel.load(); + return buildModel; + } } } return null; diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/natures/BndProject.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/natures/BndProject.java index acba547265..dce3690fe4 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/natures/BndProject.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/natures/BndProject.java @@ -25,6 +25,8 @@ public class BndProject extends BaseProject { public static final String INSTRUCTIONS_FILE = "pde" + INSTRUCTIONS_FILE_EXTENSION; //$NON-NLS-1$ + public static final String BUILDER_ID = "org.eclipse.pde.BndBuilder"; //$NON-NLS-1$ + @Override public void configure() throws CoreException { addToBuildSpec(BndBuilder.BUILDER_ID); diff --git a/ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF index ed03c0b73a..6a024241ea 100644 --- a/ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF @@ -118,9 +118,11 @@ Require-Bundle: org.eclipse.help;bundle-version="[3.10.200,4.0.0)", org.eclipse.equinox.bidi;bundle-version="[1.4.300,2.0.0)", org.eclipse.equinox.security;bundle-version="[1.4.100,2.0.0)", - org.eclipse.pde.bnd.ui;bundle-version="1.0.0" + org.eclipse.pde.bnd.ui;bundle-version="1.0.0", + org.eclipse.pde.ds.core;bundle-version="1.3.300" Import-Package: aQute.bnd.build;version="4.5.0", aQute.bnd.build.model;version="[4.2.0,5.0.0)", + aQute.bnd.build.model.clauses;version="2.5.0", aQute.bnd.header;version="[2.5.0,3.0.0)", aQute.bnd.help;version="2.0.0", aQute.bnd.osgi;version="[5.6.0,8.0.0)", diff --git a/ui/org.eclipse.pde.ui/plugin.properties b/ui/org.eclipse.pde.ui/plugin.properties index f2465f6d2e..10565a463f 100644 --- a/ui/org.eclipse.pde.ui/plugin.properties +++ b/ui/org.eclipse.pde.ui/plugin.properties @@ -267,6 +267,8 @@ command.organizeManifests.name = Organize Manifests command.organizeManifests.label= &Organize Manifests... command.organizeManifests.description = Cleans up plug-in manifest files +command.convertAutomaticManifest.label=Convert to &Automatic Manifest Generation... + command.externalizeStrings.description = Extract translatable strings from plug-in files command.externalizeStrings.name = Externalize Strings in Plug-ins command.externalizeStrings.label = E&xternalize Strings... diff --git a/ui/org.eclipse.pde.ui/plugin.xml b/ui/org.eclipse.pde.ui/plugin.xml index cfefb53d71..87b0b9a1df 100644 --- a/ui/org.eclipse.pde.ui/plugin.xml +++ b/ui/org.eclipse.pde.ui/plugin.xml @@ -1712,6 +1712,10 @@ class="org.eclipse.pde.internal.ui.wizards.tools.OrganizeManifestsAction" commandId="org.eclipse.pde.ui.organizeManifest"> + + @@ -1952,9 +1956,25 @@ + + + + + + + + - entries RemoveNodeXMLResolution_label=Remove the {0} element. RemoveNodeXMLResolution_attrLabel=Remove the {0} attribute. @@ -2315,8 +2317,10 @@ ConfigureProblemSeverityForPDECompiler_6=Open the Plug-in Development > Compiler ConfigureTargetPlatformResolution_label=Configure Target-Platform... ConfigureTargetPlatformResolution_description=Open preference page to configure the Target-Platforms. Target-Platforms describe which external artifacts are visible while developing plugins. OrganizeManifestJob_taskName=Organizing Manifest Headers... +ConvertAutomaticManifestJob_taskName=Converting to Automatic Manifest generation... OrganizeManifestsWizard_title=Organize Manifests Wizard OrganizeManifestsWizardPage_title=Organize Manifests +ConvertAutomaticManifestWizardPage_title=Convert to Automatic Manifest Generation OrganizeManifestsWizardPage_remove=&removing them OrganizeManifestsOperation_export=organizing export packages... {0} OrganizeManifestsOperation_unusedDeps=removing unused dependencies... {0} @@ -2328,6 +2332,16 @@ OrganizeManifestsOperation_nlIconPath=checking icon paths for missing $nl$ segme OrganizeManifestsOperation_unusedKeys=checking for unused keys... {0} OrganizeManifestsWizardPage_addMissing=&Ensure that all packages appear in the MANIFEST.MF OrganizeManifestsProcessor_rootMessage=Organize Manifest for {0} +ConvertAutomaticManifestsProcessor_rootMessage=Converting {0}... +ConvertAutomaticManifestsProcessor_changeProject=Convert project {0} +ConvertAutomaticManifestsWizardSettingsPage_convert_to_annotations=convert to annotations +ConvertAutomaticManifestsWizardSettingsPage_description=Please choose options below to converts your existing plugin-project into one using automatic manifest generation. +ConvertAutomaticManifestsWizardSettingsPage_manifest=Generate manifest at +ConvertAutomaticManifestsWizardSettingsPage_manifest_at_output=output folder +ConvertAutomaticManifestsWizardSettingsPage_manifest_at_root=project root +ConvertAutomaticManifestsWizardSettingsPage_discard=discard +ConvertAutomaticManifestsWizardSettingsPage_keep=keep +ConvertAutomaticManifestsWizardSettingsPage_to_instructions=to instructions OrganizeManifestsWizardPage_lazyStart=Remove unnecessary lazy activation headers OrganizeManifestsWizardPage_uselessPluginFile=Delete unnecessary plugin manifest files OrganizeManifestsOperation_filterInternal=marking export packages matching filter as internal... {0} @@ -2672,3 +2686,7 @@ ExtensionAttributeRow_AttrLabelDepr={0}(!): ExtensionAttributeRow_AttrLabelReq={0}*: ExtensionAttributeRow_AttrLabelReqDepr={0}(!)*: AnnotationHover_version_change={0} \n Reason for this version change: \n {1} +ProjectUpdateChange_configure_nature_and_builder=Update natures and builder +ProjectUpdateChange_convert_manifest_to_bnd=Convert MANIFEST.MF to bnd instructions +ProjectUpdateChange_convert_build_to_bnd=Convert build.properties to bnd instructions +ProjectUpdateChange_set_pde_preference=Set {0} in preferences diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestAction.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestAction.java new file mode 100644 index 0000000000..7090c9bec5 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestAction.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools; + +import java.util.List; +import java.util.Objects; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; +import org.eclipse.pde.internal.core.natures.BndProject; +import org.eclipse.pde.internal.core.natures.PDE; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Command handler to run the convert to automatic manifests operation. + * + */ +public class ConvertAutomaticManifestAction extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + if (PlatformUI.getWorkbench().saveAllEditors(true)) { + // only do our work when all work is committed to files... + if (HandlerUtil.getCurrentSelection(event) instanceof IStructuredSelection selection) { + List projects = selection.stream().map(ConvertAutomaticManifestAction::toProject) + .filter(Objects::nonNull) + .filter(proj -> PDE.hasPluginNature(proj)) + .filter(proj -> !BndProject.isBndProject(proj)).toList(); + if (projects.isEmpty()) { + MessageDialog.openInformation(PDEPlugin.getActiveWorkbenchShell(), + PDEUIMessages.ConvertAutomaticManifestWizardPage_title, + PDEUIMessages.OrganizeManifestsWizardPage_errorMsg); + return null; + } + RefactoringWizardOpenOperation refactoringOperation = new RefactoringWizardOpenOperation(new ConvertAutomaticManifestsWizard(projects)); + try { + refactoringOperation.run(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.ConvertAutomaticManifestWizardPage_title); + } catch (final InterruptedException e) { + // ignore... + } + } + } + return null; + } + + private static IProject toProject(Object object) { + if (object instanceof IJavaProject java) { + return java.getProject(); + } + if (object instanceof IResource resource) { + return resource.getProject(); + } + return null; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestProcessor.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestProcessor.java new file mode 100644 index 0000000000..88a6cf1d9a --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestProcessor.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; +import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; +import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor; +import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; +import org.eclipse.ltk.core.refactoring.resource.DeleteResourceChange; +import org.eclipse.osgi.service.resolver.ExportPackageDescription; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.build.IBuild; +import org.eclipse.pde.core.build.IBuildEntry; +import org.eclipse.pde.core.build.IBuildModel; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.core.natures.BndProject; +import org.eclipse.pde.internal.core.project.PDEProject; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.pde.internal.ui.wizards.tools.change.BndProjectUpdateChange; +import org.eclipse.pde.internal.ui.wizards.tools.change.BuildToBndChange; +import org.eclipse.pde.internal.ui.wizards.tools.change.CreateJarChange; +import org.eclipse.pde.internal.ui.wizards.tools.change.CreatePackageInfoChange; +import org.eclipse.pde.internal.ui.wizards.tools.change.ManifestToBndChange; +import org.eclipse.pde.internal.ui.wizards.tools.change.PreferenceChange; +import org.osgi.framework.Version; + +public class ConvertAutomaticManifestProcessor extends RefactoringProcessor { + + private List projects; + private boolean useProjectRoot; + private boolean keepRequireBundle; + private boolean keepImportPackage; + private boolean keepBREE; + private boolean keepExportPackage; + + public ConvertAutomaticManifestProcessor(List projects) { + this.projects = projects; + } + + @Override + public Object[] getElements() { + return projects.toArray(); + } + + @Override + public String getIdentifier() { + return getClass().getName(); + } + + @Override + public String getProcessorName() { + return PDEUIMessages.ConvertAutomaticManifestWizardPage_title; + } + + @Override + public boolean isApplicable() { + return true; + } + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + CompositeChange change = new CompositeChange(PDEUIMessages.ConvertAutomaticManifestWizardPage_title); + change.markAsSynthetic(); + SubMonitor subMonitor = SubMonitor.convert(pm, PDEUIMessages.ConvertAutomaticManifestJob_taskName, + projects.size()); + for (IProject project : projects) { + change.add(convertProject(project, subMonitor.split(1))); + } + return change; + } + + private Change convertProject(IProject project, IProgressMonitor monitor) throws CoreException { + CompositeChange change = new CompositeChange( + NLS.bind(PDEUIMessages.ConvertAutomaticManifestsProcessor_changeProject, project.getName())); + SubMonitor.convert(monitor, + NLS.bind(PDEUIMessages.ConvertAutomaticManifestsProcessor_rootMessage, project.getName()), 100); + IFile manifest = PDEProject.getManifest(project); + IPluginModelBase model = PluginRegistry.findModel(project); + IFile instructionsFile = project.getFile(BndProject.INSTRUCTIONS_FILE); + IBuildModel buildModel = PluginRegistry.createBuildModel(model); + change.add(new BndProjectUpdateChange(project)); + change.add(new ManifestToBndChange(project, manifest, model, instructionsFile, keepRequireBundle, + keepImportPackage, keepBREE, keepExportPackage)); + if (!keepExportPackage) { + ExportPackageDescription[] exportPackages = model.getBundleDescription().getExportPackages(); + if (exportPackages.length > 0) { + IJavaProject javaProject = JavaCore.create(project); + for (ExportPackageDescription exportPackage : exportPackages) { + Change packageInfoChange = getPackageInfoChange(exportPackage.getName(), exportPackage.getVersion(), + javaProject); + if (packageInfoChange != null) { + change.add(packageInfoChange); + } + } + } + } + IBuild build = buildModel.getBuild(); + boolean make = false; + for (IBuildEntry buildEntry : build.getBuildEntries()) { + String name = buildEntry.getName(); + if (name.startsWith(IBuildEntry.JAR_PREFIX)) { + String jarName = name.substring(IBuildEntry.JAR_PREFIX.length()); + if (jarName.equals(".")) { //$NON-NLS-1$ + continue; + } + String outputFolder = Optional.ofNullable(build.getEntry(IBuildEntry.OUTPUT_PREFIX + jarName)).stream() + .flatMap(entry -> Arrays.stream(entry.getTokens())).findFirst().orElse(null); + change.add( + new CreateJarChange(project, instructionsFile, jarName, buildEntry.getTokens(), outputFolder)); + make = true; + } + } + change.add(new BuildToBndChange(project, buildModel, instructionsFile, make)); + IFile buildProperties = PDEProject.getBuildProperties(project); + if (buildProperties.exists()) { + change.add(new DeleteResourceChange(buildProperties.getFullPath(), true)); + } + if (useProjectRoot) { + ProjectScope scope = new ProjectScope(project); + IEclipsePreferences node = scope.getNode(PDECore.PLUGIN_ID); + change.add(new PreferenceChange(node)); + } else if (manifest.exists()) { + change.add(new DeleteResourceChange(manifest.getFullPath(), true)); + } + return change; + } + + private Change getPackageInfoChange(String name, Version version, IJavaProject javaProject) throws CoreException { + IPackageFragment pkg = findPackage(javaProject, name); + if (pkg == null) { + return null; + } + ICompilationUnit cu = pkg.getCompilationUnit(CreatePackageInfoChange.PACKAGE_INFO_JAVA); + if (cu.exists()) { + // TODO we need to create an update change! + return null; + } + return new CreatePackageInfoChange(pkg, name, version); + + } + + private static IPackageFragment findPackage(IJavaProject javaProject, String name) throws JavaModelException { + for (IPackageFragment pkg : javaProject.getPackageFragments()) { + if (pkg.getKind() == IPackageFragmentRoot.K_SOURCE && pkg.getElementName().equals(name)) { + return pkg; + } + } + return null; + } + + @Override + public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) + throws CoreException { + return new RefactoringParticipant[0]; + } + + public void setUseProjectRoot(boolean useProjectRoot) { + this.useProjectRoot = useProjectRoot; + } + + public void setKeepRequireBundle(boolean keepRequireBundle) { + this.keepRequireBundle = keepRequireBundle; + } + + public void setKeepImportPackage(boolean keepImportPackage) { + this.keepImportPackage = keepImportPackage; + } + + public void setKeepRequiredExecutionEnvironment(boolean keepBREE) { + this.keepBREE = keepBREE; + } + + public void setKeepExportPackage(boolean keepExportPackage) { + this.keepExportPackage = keepExportPackage; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizard.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizard.java new file mode 100644 index 0000000000..b0f21f6c78 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizard.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools; + +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.pde.internal.ui.PDEPluginImages; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.pde.internal.ui.refactoring.PDERefactor; + +public class ConvertAutomaticManifestsWizard extends RefactoringWizard { + + public ConvertAutomaticManifestsWizard(List projects) { + super(new PDERefactor(new ConvertAutomaticManifestProcessor(projects)), WIZARD_BASED_USER_INTERFACE); + setNeedsProgressMonitor(true); + setWindowTitle(PDEUIMessages.ConvertAutomaticManifestWizardPage_title); + setDialogSettings(PDEPlugin.getDefault().getDialogSettings()); + setDefaultPageImageDescriptor(PDEPluginImages.DESC_ORGANIZE_MANIFESTS); + } + + @Override + protected void addUserInputPages() { + addPage(new ConvertAutomaticManifestsWizardSettingsPage( + (ConvertAutomaticManifestProcessor) ((PDERefactor) getRefactoring()).getProcessor())); + } + + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizardSettingsPage.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizardSettingsPage.java new file mode 100644 index 0000000000..de92e9a224 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/ConvertAutomaticManifestsWizardSettingsPage.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.Constants; + +public class ConvertAutomaticManifestsWizardSettingsPage extends UserInputWizardPage { + + private static final String SECTION_NAME = "ConvertAutomaticManifestPage"; //$NON-NLS-1$ + private static final String KEY_USE_PROJECT_ROOT = "use_project_root"; //$NON-NLS-1$ + private static final String KEY_KEEP_REQUIRE_BUNDLE = "keep_require_bundle"; //$NON-NLS-1$ + private static final String KEY_KEEP_IMPORT_PACKAGE = "keep_import_package"; //$NON-NLS-1$ + private static final String KEY_KEEP_EXPORT_PACKAGE = "keep_export_package"; //$NON-NLS-1$ + private static final String KEY_KEEP_REQUIREDEXECUTIONENVIRONMENT = "keep_requiredexecutionenvironment_package"; //$NON-NLS-1$ + private ConvertAutomaticManifestProcessor processor; + + public ConvertAutomaticManifestsWizardSettingsPage(ConvertAutomaticManifestProcessor processor) { + super(PDEUIMessages.ConvertAutomaticManifestWizardPage_title); + this.processor = processor; + setTitle(PDEUIMessages.ConvertAutomaticManifestWizardPage_title); + setDescription(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_description); + } + + @Override + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + setControl(composite); + composite.setLayout(new GridLayout(2, false)); + createGenerateOption(composite); + createRequireBundleOption(composite); + createImportPackageOption(composite); + createRequiredExecutionEnvironmentOption(composite); + createExportPackageOption(composite); + } + + private void createExportPackageOption(Composite parent) { + new Label(parent, SWT.NONE).setText(Constants.EXPORT_PACKAGE); + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + Button optionAnnotations = new Button(composite, SWT.RADIO); + optionAnnotations.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_convert_to_annotations); + Button optionKeep = new Button(composite, SWT.RADIO); + optionKeep.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_keep); + IDialogSettings settings = getSettings(); + if (settings.getBoolean(KEY_KEEP_EXPORT_PACKAGE)) { + optionKeep.setSelection(true); + processor.setKeepExportPackage(true); + } else { + optionAnnotations.setSelection(true); + } + optionAnnotations.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionAnnotations.getSelection()) { + getSettings().put(KEY_KEEP_EXPORT_PACKAGE, false); + processor.setKeepExportPackage(false); + } + })); + optionKeep.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionKeep.getSelection()) { + getSettings().put(KEY_KEEP_EXPORT_PACKAGE, true); + processor.setKeepExportPackage(true); + } + })); + } + + @SuppressWarnings("deprecation") + private void createRequiredExecutionEnvironmentOption(Composite parent) { + new Label(parent, SWT.NONE).setText(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + Button optionInstructions = new Button(composite, SWT.RADIO); + optionInstructions.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_to_instructions); + Button optionKeep = new Button(composite, SWT.RADIO); + optionKeep.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_keep); + IDialogSettings settings = getSettings(); + if (settings.getBoolean(KEY_KEEP_REQUIREDEXECUTIONENVIRONMENT)) { + optionKeep.setSelection(true); + processor.setKeepRequiredExecutionEnvironment(true); + } else { + optionInstructions.setSelection(true); + } + optionInstructions.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionInstructions.getSelection()) { + getSettings().put(KEY_KEEP_REQUIREDEXECUTIONENVIRONMENT, false); + processor.setKeepRequiredExecutionEnvironment(false); + } + })); + optionKeep.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionKeep.getSelection()) { + getSettings().put(KEY_KEEP_REQUIREDEXECUTIONENVIRONMENT, true); + processor.setKeepRequiredExecutionEnvironment(true); + } + })); + } + + private void createImportPackageOption(Composite parent) { + new Label(parent, SWT.NONE).setText(Constants.IMPORT_PACKAGE); + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + Button optionDiscard = new Button(composite, SWT.RADIO); + optionDiscard.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_discard); + Button optionKeep = new Button(composite, SWT.RADIO); + optionKeep.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_keep); + IDialogSettings settings = getSettings(); + if (settings.getBoolean(KEY_KEEP_IMPORT_PACKAGE)) { + optionKeep.setSelection(true); + processor.setKeepImportPackage(true); + } else { + optionDiscard.setSelection(true); + } + optionDiscard.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionDiscard.getSelection()) { + getSettings().put(KEY_KEEP_IMPORT_PACKAGE, false); + processor.setKeepImportPackage(false); + } + })); + optionKeep.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionKeep.getSelection()) { + getSettings().put(KEY_KEEP_IMPORT_PACKAGE, true); + processor.setKeepImportPackage(true); + } + })); + } + + private void createRequireBundleOption(Composite parent) { + new Label(parent, SWT.NONE).setText(Constants.REQUIRE_BUNDLE); + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + Button optionDiscard = new Button(composite, SWT.RADIO); + optionDiscard.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_discard); + Button optionKeep = new Button(composite, SWT.RADIO); + optionKeep.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_keep); + IDialogSettings settings = getSettings(); + if (settings.getBoolean(KEY_KEEP_REQUIRE_BUNDLE)) { + optionKeep.setSelection(true); + processor.setKeepRequireBundle(true); + } else { + optionDiscard.setSelection(true); + } + optionDiscard.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionDiscard.getSelection()) { + getSettings().put(KEY_KEEP_REQUIRE_BUNDLE, false); + processor.setKeepRequireBundle(false); + } + })); + optionKeep.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionKeep.getSelection()) { + getSettings().put(KEY_KEEP_REQUIRE_BUNDLE, true); + processor.setKeepRequireBundle(true); + } + })); + } + + private void createGenerateOption(Composite parent) { + new Label(parent, SWT.NONE).setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_manifest); + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + Button optionOutput = new Button(composite, SWT.RADIO); + optionOutput.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_manifest_at_output); + Button optionRoot = new Button(composite, SWT.RADIO); + optionRoot.setText(PDEUIMessages.ConvertAutomaticManifestsWizardSettingsPage_manifest_at_root); + IDialogSettings settings = getSettings(); + if (settings.getBoolean(KEY_USE_PROJECT_ROOT)) { + optionRoot.setSelection(true); + processor.setUseProjectRoot(true); + } else { + optionOutput.setSelection(true); + processor.setUseProjectRoot(false); + } + optionOutput.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionOutput.getSelection()) { + getSettings().put(KEY_USE_PROJECT_ROOT, false); + processor.setUseProjectRoot(false); + } + })); + optionRoot.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + if (optionRoot.getSelection()) { + getSettings().put(KEY_USE_PROJECT_ROOT, true); + processor.setUseProjectRoot(true); + } + })); + + } + + private IDialogSettings getSettings() { + IDialogSettings settings = getDialogSettings(); + IDialogSettings section = settings.getSection(SECTION_NAME); + if (section == null) { + return settings.addNewSection(SECTION_NAME); + } + return section; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BndProjectUpdateChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BndProjectUpdateChange.java new file mode 100644 index 0000000000..1ba8c0b22d --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BndProjectUpdateChange.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.pde.core.project.IBundleProjectDescription; +import org.eclipse.pde.internal.core.natures.BndProject; +import org.eclipse.pde.internal.core.natures.PDE; +import org.eclipse.pde.internal.ui.PDEUIMessages; + +public final class BndProjectUpdateChange extends Change { + + private IProject project; + + public BndProjectUpdateChange(IProject project) { + this.project = project; + } + + @Override + public String getName() { + return PDEUIMessages.ProjectUpdateChange_configure_nature_and_builder; + } + + @Override + public void initializeValidationData(IProgressMonitor pm) { + } + + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + IProjectDescription description = project.getDescription(); + String[] newNatures; + if (Arrays.stream(description.getNatureIds()) + .anyMatch(str -> "org.eclipse.pde.api.tools.apiAnalysisNature".equals(str))) { //$NON-NLS-1$ + // the API nature require the pde nature so we can only add our + // nature here... + newNatures = Stream + .concat(Arrays.stream(description.getNatureIds()), Stream.of(BndProject.NATURE_ID)) + .toArray(String[]::new); + } else { + // replace plugin with bnd nature... + newNatures = Arrays.stream(description.getNatureIds()).map(nature -> { + if (IBundleProjectDescription.PLUGIN_NATURE.equals(nature)) { + return BndProject.NATURE_ID; + } + return nature; + }).toArray(String[]::new); + } + ICommand[] commands = Stream.concat(Arrays.stream(description.getBuildSpec()).filter(command -> { + if (PDE.MANIFEST_BUILDER_ID.equals(command.getBuilderName()) + || "org.eclipse.pde.SchemaBuilder".equals(command.getBuilderName())) { //$NON-NLS-1$ + return false; + } + return true; + }), Stream.of(newBndBuilder(description))).toArray(ICommand[]::new); + description.setBuildSpec(commands); + description.setNatureIds(newNatures); + project.setDescription(description, pm); + return null; + } + + private ICommand newBndBuilder(IProjectDescription description) { + ICommand bndBuilder = description.newCommand(); + bndBuilder.setBuilderName(BndProject.BUILDER_ID); + return bndBuilder; + } + + @Override + public Object getModifiedElement() { + return project; + } + +} \ No newline at end of file diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BuildToBndChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BuildToBndChange.java new file mode 100644 index 0000000000..ea2c3097d3 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/BuildToBndChange.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.jar.JarFile; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.pde.core.build.IBuild; +import org.eclipse.pde.core.build.IBuildEntry; +import org.eclipse.pde.core.build.IBuildModel; +import org.eclipse.pde.internal.ui.PDEUIMessages; + +import aQute.bnd.build.model.BndEditModel; +import aQute.bnd.build.model.clauses.VersionedClause; +import aQute.bnd.osgi.Constants; +import aQute.bnd.properties.Document; + +public class BuildToBndChange extends Change { + + @SuppressWarnings("restriction") + private static final String DS_CONTENT_TYPE_ID = org.eclipse.pde.internal.ds.core.Activator.CONTENT_TYPE_ID; + private IBuildModel model; + private IProject project; + private IFile instructionfile; + private boolean make; + + public BuildToBndChange(IProject project, IBuildModel model, IFile instructionfile, boolean make) { + this.project = project; + this.model = model; + this.instructionfile = instructionfile; + this.make = make; + } + + @Override + public String getName() { + return PDEUIMessages.ProjectUpdateChange_convert_build_to_bnd; + } + + @Override + public void initializeValidationData(IProgressMonitor pm) { + + } + + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + if (model != null) { + Document document; + if (instructionfile.exists()) { + try (InputStream contents = instructionfile.getContents()) { + document = new Document(new String(contents.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new CoreException(Status.error("Reading file content failed", e)); //$NON-NLS-1$ + } + } else { + document = new Document(""); //$NON-NLS-1$ + } + BndEditModel editModel; + try { + editModel = new BndEditModel(document); + } catch (IOException e) { + throw new CoreException(Status.error("Reading document failed", e)); //$NON-NLS-1$ + } + IBuild build = model.getBuild(); + processBinIncludes(build, editModel); + processAdditionalBundles(build, editModel); + processExtraClasspath(build, editModel); + if (make) { + editModel.genericSet(Constants.MAKE, "(*).(jar);type=bnd; recipe=\".jars/$1.bnd\""); //$NON-NLS-1$ + } + editModel.saveChangesTo(document); + if (instructionfile.exists()) { + instructionfile.setContents(new ByteArrayInputStream(document.get().getBytes(StandardCharsets.UTF_8)), + true, true, + pm); + } else { + instructionfile.create(new ByteArrayInputStream(document.get().getBytes(StandardCharsets.UTF_8)), true, + pm); + } + } + return null; + } + + private void processBinIncludes(IBuild build, BndEditModel editModel) { + IBuildEntry entry = build.getEntry(IBuildEntry.BIN_INCLUDES); + if (entry == null) { + return; + } + List list = Arrays.stream(entry.getTokens()).filter(str -> isCustomResource(str, build)) + .map(str -> str.contains("/") ? (String.format("%s=%s", str, str)) : str) //$NON-NLS-1$ //$NON-NLS-2$ + .toList(); + // can't use editModel.addIncludeResource because of + // https://github.com/bndtools/bnd/pull/5904 + editModel.genericSet(Constants.INCLUDERESOURCE, list); + + } + + private boolean isCustomResource(String str, IBuild build) { + if (".".equals(str)) { //$NON-NLS-1$ + // this is the default jar inclusion... + return false; + } + if (JarFile.MANIFEST_NAME.equals(str)) { + // the manifest we generate! + return false; + } + if ("META-INF/".equals(str) || "META-INF".equals(str)) { //$NON-NLS-1$ //$NON-NLS-2$ + IFolder folder = project.getFolder(str); + try { + IResource[] members = folder.members(); + if (members.length == 0) { + // empty folder with manifest previously inside it + return false; + } + if (members.length == 1 && "MANIFEST.MF".equals(members[0].getName())) { //$NON-NLS-1$ + // a manifest either generated or not yet deleted... but we + // don't want to try include it + return false; + } + } catch (CoreException e) { + } + } + if ("OSGI-INF/".equals(str) || "OSGI-INF".equals(str)) { //$NON-NLS-1$ //$NON-NLS-2$ + IFolder folder = project.getFolder(str); + try { + IResource[] members = folder.members(); + if (members.length == 0) { + // empty folder then assume it is of no use... + } + for (IResource member : members) { + if (member.getName().startsWith(".")) { //$NON-NLS-1$ + continue; + } + if (member instanceof IFile file) { + IContentDescription description = file.getContentDescription(); + if (description != null) { + IContentType contentType = description.getContentType(); + if (contentType != null + && DS_CONTENT_TYPE_ID.equals(contentType.getId())) { + // a DS component... these will be generated so + // we can ignore it + continue; + } + } + return true; + } + if (member instanceof IFolder) { + // some subfolder e.g. i10n ... we need to include it + return true; + } + } + return false; + } catch (CoreException e) { + } + } + return true; + } + + private void processAdditionalBundles(IBuild build, BndEditModel editModel) { + IBuildEntry entry = build.getEntry(IBuildEntry.SECONDARY_DEPENDENCIES); + if (entry == null) { + return; + } + Arrays.stream(entry.getTokens()) + .forEach(additional -> editModel.addPath(new VersionedClause(additional), Constants.BUILDPATH)); + } + + private void processExtraClasspath(IBuild build, BndEditModel editModel) { + IBuildEntry entry = build.getEntry(IBuildEntry.JARS_EXTRA_CLASSPATH); + if (entry == null) { + return; + } + editModel.setClassPath(Arrays.asList(entry.getTokens())); + } + + @Override + public Object getModifiedElement() { + return instructionfile; + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreateJarChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreateJarChange.java new file mode 100644 index 0000000000..f7dc50243c --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreateJarChange.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.internal.ui.PDEUIMessages; + +public class CreateJarChange extends Change { + + private IFile jarInstructionFile; + private String jarName; + private IProject project; + private String[] sourceTokens; + private String outputFolder; + + public CreateJarChange(IProject project, IFile instructionsFile, String jarName, String[] sourceTokens, + String outputFolder) { + this.project = project; + this.jarName = jarName; + this.sourceTokens = sourceTokens; + this.outputFolder = outputFolder; + this.jarInstructionFile = project.getFolder(".jars") //$NON-NLS-1$ + .getFile(IPath.fromPortableString(jarName.replace(".jar", ".bnd"))); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public String getName() { + return NLS.bind(PDEUIMessages.CreateJarChange_instruction_jar, jarName); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + StringBuilder sb = new StringBuilder(); + sb.append("-includeresource: "); //$NON-NLS-1$ + sb.append(getOutputFolde()); + sb.append("\r\n"); //$NON-NLS-1$ + sb.append("-nomanifest: true\r\n"); //$NON-NLS-1$ + sb.append("-sources: false\r\n"); //$NON-NLS-1$ + mkdirs(jarInstructionFile.getParent(), pm); + if (!jarInstructionFile.exists()) { + jarInstructionFile.create(new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8)), true, + pm); + } + return null; + } + + private void mkdirs(IContainer container, IProgressMonitor pm) throws CoreException { + if (container instanceof IFolder folder) { + mkdirs(folder.getParent(), pm); + if (!folder.exists()) { + folder.create(true, true, pm); + } + } + } + + private String getOutputFolde() throws CoreException { + if (outputFolder == null) { + IJavaProject javaProject = JavaCore.create(project); + IPath outputLocation = javaProject.getOutputLocation(); + IClasspathEntry[] classpath = javaProject.getRawClasspath(); + for (IClasspathEntry entry : classpath) { + if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { + for (String token : sourceTokens) { + if (IPath.fromPortableString(token).equals(entry.getPath())) { + IPath srcLoc = entry.getOutputLocation(); + if (srcLoc != null) { + outputLocation = srcLoc; + } + break; + } + } + } + } + return project.getWorkspace().getRoot().getFolder(outputLocation).getProjectRelativePath() + .toPortableString(); + } + return outputFolder; + } + + @Override + public void initializeValidationData(IProgressMonitor pm) { + } + + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Object getModifiedElement() { + return jarInstructionFile; + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreatePackageInfoChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreatePackageInfoChange.java new file mode 100644 index 0000000000..a93671a02c --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/CreatePackageInfoChange.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.corext.util.InfoFilesUtil; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.resource.DeleteResourceChange; +import org.eclipse.ltk.core.refactoring.resource.ResourceChange; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.osgi.framework.Version; + +@SuppressWarnings("restriction") +public class CreatePackageInfoChange extends ResourceChange { + + public static final String PACKAGE_INFO_JAVA = org.eclipse.jdt.internal.corext.util.JavaModelUtil.PACKAGE_INFO_JAVA; + + private IPackageFragment fragment; + + private String name; + + private Version version; + + public CreatePackageInfoChange(IPackageFragment fragment, String name, Version version) { + this.fragment = fragment; + this.name = name; + this.version = version; + } + + @Override + protected IResource getModifiedResource() { + try { + return fragment.getCorrespondingResource(); + } catch (JavaModelException e) { + return null; + } + } + + @Override + public String getName() { + return NLS.bind(PDEUIMessages.CreatePackageInfoChange_name, PACKAGE_INFO_JAVA, fragment.getElementName()); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + StringBuilder fileContent = new StringBuilder( + "@org.osgi.annotation.bundle.Export(substitution = org.osgi.annotation.bundle.Export.Substitution.NOIMPORT)"); //$NON-NLS-1$ + if (version != null && !version.equals(Version.emptyVersion)) { + fileContent.append("@org.osgi.annotation.versioning.Version(\""); //$NON-NLS-1$ + fileContent.append(version.getMajor()); + fileContent.append('.'); + fileContent.append(version.getMinor()); + fileContent.append('.'); + fileContent.append(version.getMicro()); + fileContent.append("\")"); //$NON-NLS-1$ + } + fileContent.append("package "); //$NON-NLS-1$ + fileContent.append(name); + fileContent.append(";"); //$NON-NLS-1$ + InfoFilesUtil.createInfoJavaFile(PACKAGE_INFO_JAVA, fileContent.toString(), fragment, false, pm); + return new DeleteResourceChange(fragment.getPath(), true); + } + +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/ManifestToBndChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/ManifestToBndChange.java new file mode 100644 index 0000000000..6f7ce337ab --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/ManifestToBndChange.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.internal.core.DependencyManager; +import org.eclipse.pde.internal.ui.PDEUIMessages; + +import aQute.bnd.build.model.BndEditModel; +import aQute.bnd.build.model.EE; +import aQute.bnd.build.model.clauses.VersionedClause; +import aQute.bnd.osgi.Constants; +import aQute.bnd.properties.Document; + +public class ManifestToBndChange extends Change { + + private IFile manifestFile; + private IPluginModelBase model; + private boolean keepRequireBundle; + private boolean keepImportPackage; + private boolean keepBREE; + private boolean keepExportPackage; + private IFile instructionFile; + + public ManifestToBndChange(IProject project, IFile manifest, IPluginModelBase model, IFile instructionsFile, + boolean keepRequireBundle, + boolean keepImportPackage, + boolean keepBREE, + boolean keepExportPackage) { + this.keepRequireBundle = keepRequireBundle; + this.keepImportPackage = keepImportPackage; + this.keepBREE = keepBREE; + this.keepExportPackage = keepExportPackage; + this.manifestFile = manifest; + this.model = model; + this.instructionFile = instructionsFile; + } + + @Override + public String getName() { + return PDEUIMessages.ProjectUpdateChange_convert_manifest_to_bnd; + } + + @Override + public void initializeValidationData(IProgressMonitor pm) { + } + + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + List bundleClasspath = DependencyManager.getDependencies(List.of(model)).stream() + .map(BundleDescription::getSymbolicName).map(bsn -> new VersionedClause(bsn)).toList(); + Document document = new Document(""); //$NON-NLS-1$ + Manifest manifest; + try (InputStream contents = manifestFile.getContents()) { + manifest = new Manifest(contents); + } catch (IOException e) { + throw new CoreException(Status.error("Reading file content failed", e)); //$NON-NLS-1$ + } + BndEditModel editModel; + try { + editModel = new BndEditModel(document); + } catch (IOException e) { + throw new CoreException(Status.error("Reading document failed", e)); //$NON-NLS-1$ + } + Attributes mainAttributes = manifest.getMainAttributes(); + for (Entry entry : mainAttributes.entrySet()) { + String propertyName = entry.getKey().toString(); + if (Constants.BUNDLE_MANIFESTVERSION.equals(propertyName) || "Manifest-Version".equals(propertyName)) { //$NON-NLS-1$ + continue; + } + String string = entry.getValue().toString(); + editModel.setGenericString(propertyName, BndEditModel.format(propertyName, string)); + } + String bree = (String) editModel.genericGet(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); + if (bree != null && !keepBREE) { + try { + editModel.setEE(EE.parse(bree)); + editModel.genericSet(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, null); + } catch (Exception e) { + // if we cannot parse then keep it! + } + } + if (!keepRequireBundle) { + editModel.genericSet(Constants.REQUIRE_BUNDLE, null); + } + if (!keepImportPackage) { + editModel.genericSet(Constants.IMPORT_PACKAGE, null); + } + if (!keepExportPackage) { + editModel.genericSet(Constants.EXPORT_PACKAGE, null); + } + editModel.setBuildPath(bundleClasspath); + editModel.saveChangesTo(document); + instructionFile.create(new ByteArrayInputStream(document.get().getBytes(StandardCharsets.UTF_8)), true, pm); + return null; + } + + @Override + public Object getModifiedElement() { + return instructionFile; + } +} diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/PreferenceChange.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/PreferenceChange.java new file mode 100644 index 0000000000..b8e72399c1 --- /dev/null +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/wizards/tools/change/PreferenceChange.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.wizards.tools.change; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.internal.core.project.PDEProject; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.osgi.service.prefs.BackingStoreException; + +public class PreferenceChange extends Change { + + private IEclipsePreferences node; + + public PreferenceChange(IEclipsePreferences node) { + this.node = node; + } + + @Override + public String getName() { + return NLS.bind(PDEUIMessages.ProjectUpdateChange_set_pde_preference, PDEProject.BUNDLE_ROOT_PATH); + } + + @Override + public void initializeValidationData(IProgressMonitor pm) { + + } + + @Override + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.create(Status.OK_STATUS); + } + + @Override + public Change perform(IProgressMonitor pm) throws CoreException { + node.put(PDEProject.BUNDLE_ROOT_PATH, ""); //$NON-NLS-1$ + try { + node.flush(); + } catch (BackingStoreException e) { + } + return null; + } + + @Override + public Object getModifiedElement() { + return node; + } +}