blob: ce056486e9b80506c3c962092dbd0bd1b9530e92 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sebastian Davids <sdavids@gmx.de> bug 38692
*******************************************************************************/
package org.eclipse.jdt.internal.ui.javadocexport;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.w3c.dom.Element;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.ui.IExportWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.actions.OpenBrowserUtil;
import org.eclipse.jdt.internal.ui.dialogs.OptionalMessageDialog;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
public class JavadocWizard extends Wizard implements IExportWizard {
private JavadocTreeWizardPage fTreeWizardPage;
private JavadocSpecificsWizardPage fLastWizardPage;
private JavadocStandardWizardPage fStandardDocletWizardPage;
private ContributedJavadocWizardPage[] fContributedJavadocWizardPages;
private IPath fDestination;
private boolean fOpenInBrowser;
private final String TREE_PAGE_DESC= "JavadocTreePage"; //$NON-NLS-1$
private final String SPECIFICS_PAGE_DESC= "JavadocSpecificsPage"; //$NON-NLS-1$
private final String STANDARD_PAGE_DESC= "JavadocStandardPage"; //$NON-NLS-1$
private final int YES= 0;
private final int YES_TO_ALL= 1;
private final int NO= 2;
private final int NO_TO_ALL= 3;
private final String JAVADOC_ANT_INFORMATION_DIALOG= "javadocAntInformationDialog";//$NON-NLS-1$
private JavadocOptionsManager fStore;
private IWorkspaceRoot fRoot;
private IFile fXmlJavadocFile;
private static final String ID_JAVADOC_PROCESS_TYPE= "org.eclipse.jdt.ui.javadocProcess"; //$NON-NLS-1$
private static final String ENCODING_ARGUMENT_PREFIX= "-J-Dfile.encoding="; //$NON-NLS-1$
public static void openJavadocWizard(JavadocWizard wizard, Shell shell, IStructuredSelection selection ) {
wizard.init(PlatformUI.getWorkbench(), selection);
WizardDialog dialog= new WizardDialog(shell, wizard) {
@Override
protected IDialogSettings getDialogBoundsSettings() {
// added so that the wizard can remember the last used size
return JavaPlugin.getDefault().getDialogSettingsSection("JavadocWizardDialog"); //$NON-NLS-1$
}
};
PixelConverter converter= new PixelConverter(JFaceResources.getDialogFont());
dialog.setMinimumPageSize(converter.convertWidthInCharsToPixels(100), converter.convertHeightInCharsToPixels(20));
dialog.open();
}
public JavadocWizard() {
this(null);
}
public JavadocWizard(IFile xmlJavadocFile) {
super();
setDefaultPageImageDescriptor(JavaPluginImages.DESC_WIZBAN_EXPORT_JAVADOC);
setWindowTitle(JavadocExportMessages.JavadocWizard_javadocwizard_title);
setDialogSettings(JavaPlugin.getDefault().getDialogSettings());
fRoot= ResourcesPlugin.getWorkspace().getRoot();
fXmlJavadocFile= xmlJavadocFile;
}
/*
* @see IWizard#performFinish()
*/
@Override
public boolean performFinish() {
updateStore();
IJavaProject[] checkedProjects= fTreeWizardPage.getCheckedProjects();
fStore.updateDialogSettings(getDialogSettings(), checkedProjects);
// Wizard should not run with dirty editors
if (!new RefactoringSaveHelper(RefactoringSaveHelper.SAVE_ALL_ALWAYS_ASK).saveEditors(getShell())) {
return false;
}
fDestination= Path.fromOSString(fStore.getDestination());
fDestination.toFile().mkdirs();
fOpenInBrowser= fStore.doOpenInBrowser();
//Ask if you wish to set the javadoc location for the projects (all) to
//the location of the newly generated javadoc
if (fStore.isFromStandard()) {
try {
URL newURL= fDestination.toFile().toURI().toURL();
String newExternalForm= newURL.toExternalForm();
List<IJavaProject> projs= new ArrayList<>();
//get javadoc locations for all projects
for (IJavaProject curr : checkedProjects) {
URL currURL= JavaUI.getProjectJavadocLocation(curr);
if (currURL == null || !newExternalForm.equals(currURL.toExternalForm())) {
//if not all projects have the same javadoc location ask if you want to change
//them to have the same javadoc location
projs.add(curr);
}
}
if (!projs.isEmpty()) {
setAllJavadocLocations(projs.toArray(new IJavaProject[projs.size()]), newURL);
}
} catch (MalformedURLException e) {
JavaPlugin.log(e);
}
}
if (fLastWizardPage.generateAnt()) {
//@Improve: make a better message
OptionalMessageDialog.open(JAVADOC_ANT_INFORMATION_DIALOG, getShell(), JavadocExportMessages.JavadocWizard_antInformationDialog_title, null, JavadocExportMessages.JavadocWizard_antInformationDialog_message, MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0);
try {
Element javadocXMLElement= fStore.createXML(checkedProjects);
if (javadocXMLElement != null) {
if (!fTreeWizardPage.getCustom()) {
for (ContributedJavadocWizardPage contributedJavadocWizardPage : fContributedJavadocWizardPages) {
contributedJavadocWizardPage.updateAntScript(javadocXMLElement);
}
}
File file= fStore.writeXML(javadocXMLElement);
IFile[] files= fRoot.findFilesForLocationURI(file.toURI());
if (files != null) {
for (IFile f : files) {
f.refreshLocal(IResource.DEPTH_ONE, null);
}
}
}
} catch (CoreException e) {
ExceptionHandler.handle(e, getShell(),JavadocExportMessages.JavadocWizard_error_writeANT_title, JavadocExportMessages.JavadocWizard_error_writeANT_message);
}
}
if (!executeJavadocGeneration())
return false;
return true;
}
private void updateStore() {
fTreeWizardPage.updateStore();
if (!fTreeWizardPage.getCustom())
fStandardDocletWizardPage.updateStore();
fLastWizardPage.updateStore();
}
@Override
public boolean performCancel() {
updateStore();
//If the wizard was not launched from an ant file store the settings
if (fXmlJavadocFile == null) {
IJavaProject[] checkedProjects= fTreeWizardPage.getCheckedProjects();
fStore.updateDialogSettings(getDialogSettings(), checkedProjects);
}
return super.performCancel();
}
private void setAllJavadocLocations(IJavaProject[] projects, URL newURL) {
Shell shell= getShell();
String[] buttonlabels= new String[] { IDialogConstants.YES_LABEL, IDialogConstants.YES_TO_ALL_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.NO_TO_ALL_LABEL };
for (int j= 0; j < projects.length; j++) {
IJavaProject iJavaProject= projects[j];
String message= Messages.format(JavadocExportMessages.JavadocWizard_updatejavadoclocation_message, new String[] { BasicElementLabels.getJavaElementName(iJavaProject.getElementName()), BasicElementLabels.getPathLabel(fDestination, true) });
MessageDialog dialog= new MessageDialog(shell, JavadocExportMessages.JavadocWizard_updatejavadocdialog_label, null, message, MessageDialog.QUESTION, buttonlabels, 1);
switch (dialog.open()) {
case YES :
JavaUI.setProjectJavadocLocation(iJavaProject, newURL);
break;
case YES_TO_ALL :
for (int i= j; i < projects.length; i++) {
iJavaProject= projects[i];
JavaUI.setProjectJavadocLocation(iJavaProject, newURL);
j++;
}
break;
case NO_TO_ALL :
j= projects.length;
break;
case NO :
default :
break;
}
}
}
private boolean executeJavadocGeneration() {
Process process= null;
try {
ArrayList<String> vmArgs= new ArrayList<>();
ArrayList<String> progArgs= new ArrayList<>();
IStatus status= fStore.getArgumentArray(vmArgs, progArgs);
if (!status.isOK()) {
ErrorDialog.openError(getShell(), JavadocExportMessages.JavadocWizard_error_title, JavadocExportMessages.JavadocWizard_warning_starting_message, status);
}
if (!fTreeWizardPage.getCustom()) {
for (ContributedJavadocWizardPage fContributedJavadocWizardPage : fContributedJavadocWizardPages) {
fContributedJavadocWizardPage.updateArguments(vmArgs, progArgs);
}
}
File file= File.createTempFile("javadoc-arguments", ".tmp"); //$NON-NLS-1$//$NON-NLS-2$
vmArgs.add('@' + file.getAbsolutePath());
try (BufferedWriter writer= new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), getEncoding(vmArgs)))) {
for (int i= 0; i < progArgs.size(); i++) {
String curr= progArgs.get(i);
curr= checkForSpaces(curr);
writer.write(curr);
writer.write(' ');
}
}
String[] args= vmArgs.toArray(new String[vmArgs.size()]);
process= Runtime.getRuntime().exec(args);
if (process != null) {
// construct a formatted command line for the process properties
StringBuilder buf= new StringBuilder();
for (String arg : args) {
buf.append(arg);
buf.append(' ');
}
try {
ILaunchManager launchManager= DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType lcType= launchManager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
String name= JavadocExportMessages.JavadocWizard_launchconfig_name;
ILaunchConfigurationWorkingCopy wc= lcType.newInstance(null, name);
wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
ILaunch newLaunch= new Launch(wc, ILaunchManager.RUN_MODE, null);
IProcess iprocess= DebugPlugin.newProcess(newLaunch, process, JavadocExportMessages.JavadocWizard_javadocprocess_label);
iprocess.setAttribute(IProcess.ATTR_CMDLINE, buf.toString());
iprocess.setAttribute(IProcess.ATTR_PROCESS_TYPE, ID_JAVADOC_PROCESS_TYPE);
launchManager.addLaunch(newLaunch);
JavadocLaunchListener listener= new JavadocLaunchListener(getShell().getDisplay(), newLaunch, file);
launchManager.addLaunchListener(listener);
if (newLaunch.isTerminated()) {
listener.onTerminated();
}
} catch (CoreException e) {
String title= JavadocExportMessages.JavadocWizard_error_title;
String message= JavadocExportMessages.JavadocWizard_launch_error_message;
ExceptionHandler.handle(e, getShell(), title, message);
}
return true;
}
} catch (IOException e) {
String title= JavadocExportMessages.JavadocWizard_error_title;
String message= JavadocExportMessages.JavadocWizard_exec_error_message;
IStatus status= new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IStatus.ERROR, e.getMessage(), e);
ExceptionHandler.handle(new CoreException(status), getShell(), title, message);
return false;
}
return false;
}
private static String getEncoding(ArrayList<String> vmArgs) {
Iterator<String> iter= vmArgs.iterator();
while (iter.hasNext()) {
String argument= iter.next();
if (argument.length() > ENCODING_ARGUMENT_PREFIX.length() && argument.startsWith(ENCODING_ARGUMENT_PREFIX)) {
String encoding= argument.substring(ENCODING_ARGUMENT_PREFIX.length());
if (Charset.isSupported(encoding))
return encoding;
break;
}
}
return System.getProperty("file.encoding"); //$NON-NLS-1$
}
private String checkForSpaces(String curr) {
if (curr.indexOf(' ') == -1) {
return curr;
}
StringBuilder buf= new StringBuilder();
buf.append('\'');
for (int i= 0; i < curr.length(); i++) {
char ch= curr.charAt(i);
if (ch == '\\' || ch == '\'') {
buf.append('\\');
}
buf.append(ch);
}
buf.append('\'');
return buf.toString();
}
/*
* @see IWizard#addPages()
*/
@Override
public void addPages() {
fContributedJavadocWizardPages= ContributedJavadocWizardPage.getContributedPages(fStore);
fTreeWizardPage= new JavadocTreeWizardPage(TREE_PAGE_DESC, fStore);
fLastWizardPage= new JavadocSpecificsWizardPage(SPECIFICS_PAGE_DESC, fTreeWizardPage, fStore);
fStandardDocletWizardPage= new JavadocStandardWizardPage(STANDARD_PAGE_DESC, fTreeWizardPage, fStore);
super.addPage(fTreeWizardPage);
super.addPage(fStandardDocletWizardPage);
for (ContributedJavadocWizardPage fContributedJavadocWizardPage : fContributedJavadocWizardPages) {
super.addPage(fContributedJavadocWizardPage);
}
super.addPage(fLastWizardPage);
fTreeWizardPage.init();
fStandardDocletWizardPage.init();
fLastWizardPage.init();
}
@Override
public void init(IWorkbench workbench, IStructuredSelection structuredSelection) {
IWorkbenchWindow window= workbench.getActiveWorkbenchWindow();
List<?> selected= Collections.EMPTY_LIST;
if (window != null) {
ISelection selection= window.getSelectionService().getSelection();
if (selection instanceof IStructuredSelection) {
selected= ((IStructuredSelection) selection).toList();
} else {
IJavaElement element= EditorUtility.getActiveEditorJavaInput();
if (element != null) {
selected= Arrays.asList(element);
}
}
}
fStore= new JavadocOptionsManager(fXmlJavadocFile, getDialogSettings(), selected);
}
private void refresh(IPath path) {
IContainer[] containers= fRoot.findContainersForLocationURI(path.toFile().toURI());
try {
for (IContainer container : containers) {
container.refreshLocal(IResource.DEPTH_INFINITE, null);
}
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
private void spawnInBrowser(Display display) {
if (fOpenInBrowser) {
try {
IPath indexFile= fDestination.append("index.html"); //$NON-NLS-1$
URL url= indexFile.toFile().toURI().toURL();
OpenBrowserUtil.open(url, display);
} catch (MalformedURLException e) {
JavaPlugin.log(e);
}
}
}
private class JavadocLaunchListener implements ILaunchesListener2 {
private Display fDisplay;
private volatile ILaunch fLaunch;
private File fFile;
public JavadocLaunchListener(Display display, ILaunch launch, File file) {
fDisplay= display;
fLaunch= launch;
fFile= file;
}
@Override
public void launchesTerminated(ILaunch[] launches) {
if (containsJavadocLaunch(launches)) {
onTerminated();
}
}
private boolean containsJavadocLaunch(ILaunch[] launches) {
for (ILaunch launch : launches) {
if (launch == fLaunch) {
return true;
}
}
return false;
}
public void onTerminated() {
if (fLaunch != null) {
fFile.deleteOnExit(); // Just to be safe. #launchesRemoved(..) is not called on shutdown.
spawnInBrowser(fDisplay);
refresh(fDestination);
}
}
@Override
public void launchesRemoved(ILaunch[] launches) {
if (containsJavadocLaunch(launches)) {
try {
fFile.delete();
fLaunch= null;
} finally {
DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
}
}
}
@Override
public void launchesAdded(ILaunch[] launches) { }
@Override
public void launchesChanged(ILaunch[] launches) { }
}
@Override
public IWizardPage getNextPage(IWizardPage page) {
if (page == fTreeWizardPage && fTreeWizardPage.getCustom()) {
return fLastWizardPage;
}
return super.getNextPage(page);
}
@Override
public IWizardPage getPreviousPage(IWizardPage page) {
if (page == fLastWizardPage && fTreeWizardPage.getCustom()) {
return fTreeWizardPage;
}
return super.getPreviousPage(page);
}
}