### Eclipse Workspace Patch 1.0 #P org.eclipse.pde.ui Index: plugin.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/plugin.properties,v retrieving revision 1.215 diff -u -r1.215 plugin.properties --- plugin.properties 11 Aug 2008 16:47:10 -0000 1.215 +++ plugin.properties 21 Sep 2008 19:20:23 -0000 @@ -250,6 +250,8 @@ pluginsearch.action.name = Open Plug-in Artifact pluginsearch.action.menu.name = Open &Plug-in Artifact... pluginsearch.action.desc = Open a plug-in artifact in the manifest editor + +Internationalize.label = Internationalize... markerGroupingEntry.label = Plug-in Development showErrorInStackTraceConsoleAction.label = Show in Stack Trace Console View -showErrorInStackTraceConsoleAction.tooltip = Display the stack trace from the currently selected error in the java stack trace console \ No newline at end of file +showErrorInStackTraceConsoleAction.tooltip = Display the stack trace from the currently selected error in the java stack trace console>>>>>>> 1.215 Index: plugin.xml =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/plugin.xml,v retrieving revision 1.444 diff -u -r1.444 plugin.xml --- plugin.xml 15 Sep 2008 15:08:18 -0000 1.444 +++ plugin.xml 21 Sep 2008 19:20:24 -0000 @@ -578,6 +578,14 @@ name="group1"> + + + + - + + + fSelection are to be externalized. + private boolean fExternalizeSelectedPluginsOnly; + + public GetNonExternalizedStringsOperation(ISelection selection, boolean externalizeSelectedPluginsOnly) { fSelection = selection; + fExternalizeSelectedPluginsOnly = externalizeSelectedPluginsOnly; } public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { @@ -56,12 +61,29 @@ fModelChangeTable = new ModelChangeTable(); - IPluginModelBase[] pluginModels = PluginRegistry.getWorkspaceModels(); - monitor.beginTask(PDEUIMessages.GetNonExternalizedStringsOperation_taskMessage, pluginModels.length); - for (int i = 0; i < pluginModels.length && !fCanceled; i++) { - IProject project = pluginModels[i].getUnderlyingResource().getProject(); - if (!WorkspaceModelManager.isBinaryProject(project)) - getUnExternalizedStrings(project, new SubProgressMonitor(monitor, 1)); + /* + * Azure: This will add only the preselected plug-ins to the ModelChangeTable + * instead of adding the list of all plug-ins in the workspace. This is useful + * when the Internationalize action is run on a set of non-externalized plug-ins + * where there is no need to display all non-externalized plug-ins in the + * workspace, but only those selected. + */ + if (fExternalizeSelectedPluginsOnly) { + monitor.beginTask(PDEUIMessages.GetNonExternalizedStringsOperation_taskMessage, fSelectedModels.size()); + Iterator iterator = fSelectedModels.iterator(); + while (iterator.hasNext() && !fCanceled) { + IProject project = (IProject) iterator.next(); + if (!WorkspaceModelManager.isBinaryProject(project)) + getUnExternalizedStrings(project, new SubProgressMonitor(monitor, 1)); + } + } else { + IPluginModelBase[] pluginModels = PluginRegistry.getWorkspaceModels(); + monitor.beginTask(PDEUIMessages.GetNonExternalizedStringsOperation_taskMessage, pluginModels.length); + for (int i = 0; i < pluginModels.length && !fCanceled; i++) { + IProject project = pluginModels[i].getUnderlyingResource().getProject(); + if (!WorkspaceModelManager.isBinaryProject(project)) + getUnExternalizedStrings(project, new SubProgressMonitor(monitor, 1)); + } } } } Index: src/org/eclipse/pde/internal/ui/nls/GetNonExternalizedStringsAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/nls/GetNonExternalizedStringsAction.java,v retrieving revision 1.8 diff -u -r1.8 GetNonExternalizedStringsAction.java --- src/org/eclipse/pde/internal/ui/nls/GetNonExternalizedStringsAction.java 16 Jan 2008 17:08:33 -0000 1.8 +++ src/org/eclipse/pde/internal/ui/nls/GetNonExternalizedStringsAction.java 21 Sep 2008 19:20:29 -0000 @@ -23,12 +23,22 @@ public class GetNonExternalizedStringsAction implements IWorkbenchWindowActionDelegate { private ISelection fSelection; + //Azure: To indicate that only selected plug-ins are to be externalized. False by default. + private boolean fExternalizeSelectedPluginsOnly = false; + + //Azure: To indicate that the post-externalization message dialog should not be displayed. + private boolean fSkipMessageDialog = false; public GetNonExternalizedStringsAction() { } public void run(IAction action) { - GetNonExternalizedStringsOperation runnable = new GetNonExternalizedStringsOperation(fSelection); + /* + * Azure: Pass fExternalizeSelectedPluginsOnly to the operation to indicate + * that only the plug-ins passed in the selection are to be externalized and such that + * only those are displayed on the change table in the ExternalizeStringsWizard. + */ + GetNonExternalizedStringsOperation runnable = new GetNonExternalizedStringsOperation(fSelection, fExternalizeSelectedPluginsOnly); try { PlatformUI.getWorkbench().getProgressService().busyCursorWhile(runnable); } catch (InvocationTargetException e) { @@ -47,8 +57,15 @@ op.run(PDEPlugin.getActiveWorkbenchShell(), ""); //$NON-NLS-1$ } catch (final InterruptedException irex) { } - } else - MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), PDEUIMessages.GetNonExternalizedStringsAction_allExternalizedTitle, PDEUIMessages.GetNonExternalizedStringsAction_allExternalizedMessage); + } else { + /* + * Azure: When the InternationalizeAction invokes the ExternalizeStringsAction, + * fSkipMessageDialog is set to true in order for no intermediate + * message to appear if all selected plug-ins were already externalized. + */ + if (!fSkipMessageDialog) + MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), PDEUIMessages.GetNonExternalizedStringsAction_allExternalizedTitle, PDEUIMessages.GetNonExternalizedStringsAction_allExternalizedMessage); + } } } @@ -61,4 +78,37 @@ public void init(IWorkbenchWindow window) { } + + + /** + * TODO: Azure Documentation + * @param externalizeSelectedPluginsOnly + */ + public void setExternalizeSelectedPluginsOnly(boolean externalizeSelectedPluginsOnly) { + fExternalizeSelectedPluginsOnly = externalizeSelectedPluginsOnly; + } + + /** + * TODO: Azure Documentation + * @return + */ + public boolean isExternalizeSelectedPluginsOnly() { + return fExternalizeSelectedPluginsOnly; + } + + /** + * TODO: Azure Documentation + * @param skipMessageDialog + */ + public void setSkipMessageDialog(boolean skipMessageDialog) { + this.fSkipMessageDialog = skipMessageDialog; + } + + /** + * TODO: Azure Documentation + * @return + */ + public boolean isSkipMessageDialog() { + return fSkipMessageDialog; + } } Index: src/org/eclipse/pde/internal/ui/nls/InternationalizationWizardPage.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizationWizardPage.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizationWizardPage.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizationWizardPage.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.*; + +public abstract class InternationalizationWizardPage extends WizardPage { + + public InternationalizationWizardPage(String pageName) { + super(pageName); + } + + public InternationalizationWizardPage(String pageName, String title, ImageDescriptor titleImage) { + super(pageName, title, titleImage); + } + + protected Group createFilterContainer(Composite parent, String title, String label) { + Group container = new Group(parent, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = layout.marginHeight = 6; + container.setLayout(layout); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + container.setLayoutData(gd); + container.setText(title); + + Label templateLabel = new Label(container, SWT.NONE); + templateLabel.setText(label); + return container; + } + + protected Text createFilterText(Composite parent, String initial) { + Text text = new Text(parent, SWT.BORDER); + text.setText(initial); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + text.setLayoutData(gd); + return text; + } + +} Index: src/org/eclipse/pde/internal/ui/nls/NLSFragmentGenerator.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/NLSFragmentGenerator.java diff -N src/org/eclipse/pde/internal/ui/nls/NLSFragmentGenerator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/NLSFragmentGenerator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,543 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.*; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.internal.core.TargetPlatformHelper; +import org.eclipse.pde.internal.core.plugin.ExternalPluginModelBase; +import org.eclipse.pde.internal.ui.*; +import org.eclipse.pde.internal.ui.wizards.IProjectProvider; +import org.eclipse.pde.internal.ui.wizards.plugin.FragmentFieldData; +import org.eclipse.pde.internal.ui.wizards.plugin.NewProjectCreationOperation; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * + * Generates the fragment projects for the list of selected plug-ins. + * Each fragment contains a series of properties files specific to the locales + * the user selected. For example plugin_fr.properties for the french locale + * selection. The generated locale-specific properties files contain the same + * key-value pairs as the properties file of the initial plug-in. + * + * @author Team Azure + * + */ +public class NLSFragmentGenerator { + public static final String PLUGIN_NAME_MACRO = "${plugin_name}"; //$NON-NLS-1$ + public static final String LOCALE_NAME_MACRO = "${locale}"; //$NON-NLS-1$ + + private static final String HTML_EXTENSION = ".html"; //$NON-NLS-1$ + private static final String XML_EXTENSION = ".xml"; //$NON-NLS-1$ + private static final String CLASS_EXTENSION = ".class"; //$NON-NLS-1$ + private static final String JAVA_EXTENSION = ".java"; //$NON-NLS-1$ + private static final String PROPERTIES_EXTENSION = ".properties"; //$NON-NLS-1$ + private static final String JAR_EXTENSION = ".jar"; //$NON-NLS-1$ + + private static final String PLUGIN_XML = "plugin.xml"; //$NON-NLS-1$ + private static final String BUILD_PROPERTIES = "build.properties"; //$NON-NLS-1$ + + private static final String BIN = "/bin/"; //$NON-NLS-1$ + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + private static final String BACKSLASH = "\\"; //$NON-NLS-1$ + private static final String SLASH = "/"; //$NON-NLS-1$ + private static final String RESOURCE_FOLDER_PARENT = "/nl"; //$NON-NLS-1$ + private static final double LATEST_ECLIPSE_VERSION = 3.4; + + private static final String ZERO = "0"; //$NON-NLS-1$ + private static final String PERIOD = "."; //$NON-NLS-1$ + private static final String MIN_MINOR = ZERO; + private static final String MAX_MINOR = "9"; //$NON-NLS-1$ + private static final String LEFT_SQUARE_BRACKET = "["; //$NON-NLS-1$ + private static final String RIGHT_PARENTHESIS = ")"; //$NON-NLS-1$ + private static final String DEFAULT_VERSION = "1.0.0"; //$NON-NLS-1$ + private static final String VERSION_FORMAT_WITH_QUALIFIER = "\\d+\\.\\d+\\.\\d+\\..+"; //$NON-NLS-1$ + private static final String LOCALE_INFIX_SEPERATOR = "_"; //$NON-NLS-1$ + + private final IWizardContainer container; + private final String template; + private final List plugins; + private final List locales; + private final boolean overwriteWithoutAsking; + private IProgressMonitor monitor; + + private final Filters resourceFilter = new Filters(false) { + { + add(new AbstractFilter(false) { + public boolean matches(Object object) { + String resource = object.toString(); + return resource.endsWith(PROPERTIES_EXTENSION) || resource.endsWith(CLASS_EXTENSION) || resource.endsWith(JAVA_EXTENSION); + } + }); + + add(new AbstractFilter(false) { + public boolean matches(Object object) { + String path = object.toString(); + return path.indexOf(BIN) != -1 || path.endsWith(SLASH) || path.endsWith(PLUGIN_XML); + } + }); + + add(new AbstractFilter(true) { + public boolean matches(Object object) { + String path = object.toString(); + return path.endsWith(XML_EXTENSION) || path.endsWith(HTML_EXTENSION); + } + }); + } + }; + + private final Filters propertiesFilter = new Filters(false) { + { + add(new AbstractFilter(false) { + public boolean matches(Object object) { + String path = object.toString(); + return path.indexOf(BIN) != -1 || path.endsWith(BUILD_PROPERTIES); + } + }); + + add(new AbstractFilter(true) { + public boolean matches(Object object) { + return object.toString().endsWith(PROPERTIES_EXTENSION); + } + }); + } + }; + + public NLSFragmentGenerator(String template, List plugins, List locales, IWizardContainer container, boolean overwriteWithoutAsking) { + this.plugins = plugins; + this.locales = locales; + this.container = container; + this.template = template; + this.overwriteWithoutAsking = overwriteWithoutAsking; + } + + private synchronized void setProgressMonitor(IProgressMonitor monitor) { + this.monitor = monitor; + } + + private synchronized IProgressMonitor getProgressMonitor() { + return monitor; + } + + public boolean generate() { + try { + final Map overwrites = promptForOverwrite(plugins, locales); + + container.run(false, false, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + setProgressMonitor(monitor); + try { + internationalizePlugins(plugins, locales, overwrites); + } catch (final Exception ex) { + Display.getDefault().syncExec(new Runnable() { + public void run() { + IStatus status = new Status(IStatus.ERROR, IPDEUIConstants.PLUGIN_ID, PDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_errorMessage, ex); + PDEPlugin.getDefault().getLog().log(status); + ErrorDialog.openError(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_errorTitle, null, status); + ex.printStackTrace(); + } + }); + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + return true; + } + + /** + * Creates an NL fragment project along with the locale specific properties + * files. + * @throws CoreException + * @throws IOException + * @throws InvocationTargetException + * @throws InterruptedException + */ + private void internationalizePlugins(List plugins, List locales, Map overwrites) throws CoreException, IOException, InvocationTargetException, InterruptedException { + + Set created = new HashSet(); + + for (Iterator it = plugins.iterator(); it.hasNext();) { + IPluginModelBase plugin = (IPluginModelBase) it.next(); + + for (Iterator iter = locales.iterator(); iter.hasNext();) { + Locale locale = (Locale) iter.next(); + + IProject project = getNLProject(plugin, locale); + if (created.contains(project) || overwriteWithoutAsking || !project.exists() || OVERWRITE == overwrites.get(project.getName())) { + if (!created.contains(project) && project.exists()) { + project.delete(true, getProgressMonitor()); + } + + if (!created.contains(project)) { + createNLFragment(plugin, project, locale); + created.add(project); + project.getFolder(RESOURCE_FOLDER_PARENT).create(false, true, getProgressMonitor()); + } + + project.getFolder(RESOURCE_FOLDER_PARENT).getFolder(locale.toString()).create(true, true, getProgressMonitor()); + createLocaleSpecificPropertiesFile(project, plugin, locale); + } + } + } + } + + private Object OVERWRITE = new Object(); + + private Map promptForOverwrite(List plugins, List locales) { + Map overwrites = new HashMap(); + + if (overwriteWithoutAsking) + return overwrites; + + for (Iterator iter = plugins.iterator(); iter.hasNext();) { + IPluginModelBase plugin = (IPluginModelBase) iter.next(); + for (Iterator it = locales.iterator(); it.hasNext();) { + Locale locale = (Locale) it.next(); + IProject project = getNLProject(plugin, locale); + + if (project.exists() && !overwrites.containsKey(project.getName())) { + boolean overwrite = MessageDialog.openConfirm(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_overwriteTitle, NLS.bind(PDEUIMessages.InternationalizeWizard_NLSFragmentGenerator_overwriteMessage, pluginName(plugin, locale))); + overwrites.put(project.getName(), overwrite ? OVERWRITE : null); + } + } + } + + return overwrites; + } + + private IProject getNLProject(final IPluginModelBase name, final Locale locale) { + return ResourcesPlugin.getWorkspace().getRoot().getProject(pluginName(name, locale)); + } + + /** + * Creates a fragment project for the specified plug-in and populates + * the field data. + * @param plugin + * @throws CoreException + * @throws InvocationTargetException + * @throws InterruptedException + */ + private void createNLFragment(final IPluginModelBase plugin, final IProject project, final Locale locale) throws CoreException, InvocationTargetException, InterruptedException { + FragmentFieldData fragmentData = populateFieldData(plugin, locale); + + IProjectProvider projectProvider = new IProjectProvider() { + public String getProjectName() { + return project.getName(); + } + + public IProject getProject() { + return project; + } + + public IPath getLocationPath() { + return project.getLocation(); + } + }; + + new NewProjectCreationOperation(fragmentData, projectProvider, null).run(getProgressMonitor()); + } + + private String pluginName(IPluginModelBase plugin, Locale locale) { + return template.replaceAll(quote(PLUGIN_NAME_MACRO), plugin.getPluginBase().getId()).replaceAll(quote(LOCALE_NAME_MACRO), locale.toString()); + } + + /** + * The fields are populated based on the plug-in attributes. Some fields + * are set to their default values. + */ + private FragmentFieldData populateFieldData(IPluginModelBase plugin, Locale locale) { + FragmentFieldData fragmentData = new FragmentFieldData(); + + fragmentData.setId(pluginName(plugin, locale)); + fragmentData.setVersion(DEFAULT_VERSION); + fragmentData.setMatch(0); + + fragmentData.setPluginId(plugin.getPluginBase().getId()); + fragmentData.setPluginVersion(incrementRelease(plugin.getPluginBase().getVersion())); + fragmentData.setName(pluginName(plugin, locale) + " Fragment"); //$NON-NLS-1$ + fragmentData.setProvider(EMPTY_STRING); + fragmentData.setSimple(true); + + if (!(plugin instanceof ExternalPluginModelBase)) { + fragmentData.setSourceFolderName("src"); //$NON-NLS-1$ + fragmentData.setOutputFolderName("bin"); //$NON-NLS-1$ + } + + fragmentData.setLegacy(false); + fragmentData.setTargetVersion(Double.toString(ensureTargetVersionCompatibility(TargetPlatformHelper.getTargetVersion()))); + fragmentData.setHasBundleStructure(true); + fragmentData.setOSGiFramework(null); + fragmentData.setWorkingSets(null); + + return fragmentData; + } + + /** + * Adjusts the plug-in's version to reflect the required + * fragment-host bundle-version range. For example, + * fragment-host's bundle-version range would be: "[1.0.0, 1.1.0)" + * if the host's version is 1.0.0 + * @param oldVersion + * @return adjusted plug-in version + */ + private String incrementRelease(String oldVersion) { + if (oldVersion.matches(VERSION_FORMAT_WITH_QUALIFIER)) { + oldVersion = oldVersion.substring(0, oldVersion.lastIndexOf(PERIOD)); + } + + String newVersion = LEFT_SQUARE_BRACKET + oldVersion + ','; + String oldMinor = oldVersion.substring(oldVersion.indexOf(PERIOD) + 1, oldVersion.lastIndexOf(PERIOD)); + + if (oldMinor.compareTo(MAX_MINOR) == 0) { + String major = Integer.toString(Integer.parseInt(oldVersion.substring(0, oldVersion.indexOf(PERIOD))) + 1); + newVersion += major + PERIOD + MIN_MINOR + PERIOD + ZERO + RIGHT_PARENTHESIS; + } else { + String major = oldVersion.substring(0, oldVersion.indexOf(PERIOD)); + String newMinor = Integer.toString(Integer.parseInt(oldMinor) + 1); + newVersion += major + PERIOD + newMinor + PERIOD + ZERO + RIGHT_PARENTHESIS; + } + + return newVersion; + } + + /** + * Creates a locale specific properties file within the fragment project + * based on the content of the host plug-in's properties file. + * @param fragmentProject + * @param locale + * @throws CoreException + * @throws IOException + */ + private void createLocaleSpecificPropertiesFile(final IProject fragmentProject, IPluginModelBase plugin, final Locale locale) throws CoreException, IOException { + final IFolder localeResourceFolder = fragmentProject.getFolder(RESOURCE_FOLDER_PARENT).getFolder(locale.toString()); + + //Case 1: External plug-in + if (plugin instanceof ExternalPluginModelBase) { + final String installLocation = plugin.getInstallLocation(); + //Case 1a: External plug-in is a jar file + if (installLocation.endsWith(JAR_EXTENSION)) { + ZipFile zf = new ZipFile(installLocation); + for (Enumeration e = zf.entries(); e.hasMoreElements();) { + worked(); + + ZipEntry zfe = (ZipEntry) e.nextElement(); + String name = zfe.getName(); + + String[] segments = name.split(SLASH); + IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1)); + String resourceName = segments[segments.length - 1]; + String localizedResourceName = localeSpecificName(resourceName, locale); + if (propertiesFilter.include(name)) { + + createParents(fragmentProject, path); + IFile file = fragmentProject.getFile(path.append(localizedResourceName)); + InputStream is = zf.getInputStream(zfe); + file.create(is, false, getProgressMonitor()); + } else if (resourceFilter.include(name)) { + IPath target = localeResourceFolder.getFullPath().append(path).append(resourceName); + createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1)); + IFile file = fragmentProject.getFile(target.removeFirstSegments(1)); + file.create(zf.getInputStream(zfe), false, getProgressMonitor()); + } + } + } + //Case 1b: External plug-in has a folder structure + else { + Visitor visitor = new Visitor() { + public void visit(File file) throws CoreException, FileNotFoundException { + worked(); + + String relativePath = file.getAbsolutePath().substring(installLocation.length()).replaceAll(File.separator, SLASH); + String[] segments = relativePath.split(SLASH); + IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1)); + String resourceName = segments[segments.length - 1]; + String localizedResourceName = localeSpecificName(resourceName, locale); + + if (propertiesFilter.include(relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) { + createParents(fragmentProject, path); + IFile iFile = fragmentProject.getFile(path.append(localizedResourceName)); + iFile.create(new FileInputStream(file), false, getProgressMonitor()); + } else if (resourceFilter.include(relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) { + IPath target = localeResourceFolder.getFullPath().append(relativePath); + createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1)); + IFile iFile = fragmentProject.getFile(target.removeFirstSegments(1)); + iFile.create(new FileInputStream(file), false, getProgressMonitor()); + } + + if (file.isDirectory()) { + File[] children = file.listFiles(); + for (int i = 0; i < children.length; i++) { + visit(children[i]); + } + } + } + }; + + visitor.visit(new File(installLocation)); + } + } + //Case 2: Workspace plug-in + else { + final IProject project = plugin.getUnderlyingResource().getProject(); + + project.accept(new IResourceVisitor() { + public boolean visit(IResource resource) throws CoreException { + worked(); + + IPath parent = resource.getFullPath().removeLastSegments(1).removeFirstSegments(1); + if (propertiesFilter.include(resource)) { + String segment = localeSpecificName(resource.getFullPath().lastSegment(), locale); + IPath fragmentResource = fragmentProject.getFullPath().append(parent).append(segment); + + createParents(fragmentProject, parent); + resource.copy(fragmentResource, true, getProgressMonitor()); + } else if (resourceFilter.include(resource)) { + IPath target = localeResourceFolder.getFullPath().append(parent).append(resource.getFullPath().lastSegment()); + createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1)); + resource.copy(target, true, getProgressMonitor()); + } + return true; + } + }); + } + + } + + private void worked() { + Shell shell = container.getShell(); + Display display = shell.getDisplay(); + if (display != null && !shell.isDisposed()) { + display.readAndDispatch(); + } + } + + private void createParents(IProject fragmentProject, IPath parent) throws CoreException { + String[] segments = parent.segments(); + String path = new String(); + + for (int i = 0; i < segments.length; i++) { + path += SLASH + segments[i]; + IFolder folder = fragmentProject.getFolder(path); + if (!folder.exists()) { + folder.create(true, true, getProgressMonitor()); + } + } + } + + private String join(String delimiter, String[] parts) { + return join(delimiter, parts, 0, parts.length); + } + + private String join(String delimiter, String[] parts, int offset, int n) { + StringBuffer builder = new StringBuffer(); + for (int i = offset; i < n; i++) { + builder.append(parts[i]); + if (i < parts.length - 1) { + builder.append(delimiter); + } + } + return builder.toString(); + } + + private String localeSpecificName(String name, Locale locale) { + String[] parts = name.split(BACKSLASH + PERIOD); + parts[0] = parts[0] + LOCALE_INFIX_SEPERATOR + locale; + return join(PERIOD, parts); + } + + private static class Filters { + private final List filters = new LinkedList(); + private final boolean default_; + + public Filters(boolean default_) { + this.default_ = default_; + } + + public void add(Filter filter) { + filters.add(filter); + } + + public void remove(Filter filter) { + filters.remove(filter); + } + + public boolean include(Object object) { + if (object instanceof IResource) { + IResource resource = (IResource) object; + IPath path = IResource.FILE == resource.getType() ? resource.getFullPath() : resource.getFullPath().addTrailingSeparator(); + object = path.toPortableString(); + } + + for (Iterator iter = filters.iterator(); iter.hasNext();) { + Filter filter = (Filter) iter.next(); + if (filter.matches(object)) { + return filter.inclusive(); + } + } + return default_; + } + } + + private static abstract class AbstractFilter implements Filter { + + private final boolean inclusive; + + public AbstractFilter(boolean inclusive) { + this.inclusive = inclusive; + + } + + public boolean inclusive() { + return inclusive; + } + } + + private static interface Filter { + boolean inclusive(); + + boolean matches(Object object); + } + + private static interface Visitor { + void visit(File file) throws CoreException, FileNotFoundException; + } + + private String quote(String pattern) { + return "\\Q" + pattern + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Ensures that the target version is compatible. + * @param targetVersion + * @return target version + */ + private double ensureTargetVersionCompatibility(double targetVersion) { + if (targetVersion < 3.0) { + return LATEST_ECLIPSE_VERSION; + } + return targetVersion; + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardOpenOperation.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardOpenOperation.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardOpenOperation.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardOpenOperation.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Shell; + +/** + * A helper class to open an InternationalizeWizard dialog. + * + * @author Team Azure + */ +public class InternationalizeWizardOpenOperation { + + private InternationalizeWizard fWizard; + + public InternationalizeWizardOpenOperation(InternationalizeWizard wizard) { + Assert.isNotNull(wizard); + fWizard = wizard; + } + + public int run(final Shell parent, final String dialogTitle) throws InterruptedException { + Assert.isNotNull(dialogTitle); + final IJobManager manager = Job.getJobManager(); + final int[] result = new int[1]; + final InterruptedException[] canceled = new InterruptedException[1]; + + Runnable r = new Runnable() { + public void run() { + try { + manager.beginRule(ResourcesPlugin.getWorkspace().getRoot(), null); + + Dialog dialog = new WizardDialog(parent, fWizard); + dialog.create(); + + IWizardContainer wizardContainer = (IWizardContainer) dialog; + if (wizardContainer.getCurrentPage() == null) { + //Close the dialog if there are no pages + result[0] = Window.CANCEL; + } else { + //Open the wizard dialog + result[0] = dialog.open(); + } + + } catch (OperationCanceledException e) { + canceled[0] = new InterruptedException(e.getMessage()); + } finally { + manager.endRule(ResourcesPlugin.getWorkspace().getRoot()); + } + } + }; + BusyIndicator.showWhile(parent.getDisplay(), r); + if (canceled[0] != null) + throw canceled[0]; + return result[0]; + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeAction.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeAction.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeAction.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeAction.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.ui.nls; + +import java.lang.reflect.InvocationTargetException; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.ui.*; + +/** + * This action class is responsible for creating and initializing the + * InternationalizeWizard. + * + * @author Team Azure + * + */ +public class InternationalizeAction implements IWorkbenchWindowActionDelegate { + + private IStructuredSelection fSelection; + + public InternationalizeAction() { + } + + public void run(IAction action) { + //Create an InternationalizeOperation on the workbench selection. + InternationalizeOperation runnable = new InternationalizeOperation(fSelection); + try { + PlatformUI.getWorkbench().getProgressService().busyCursorWhile(runnable); + } catch (InvocationTargetException e) { + } catch (InterruptedException e) { + } finally { + if (runnable.wasCanceled()) { + return; + } + + /* Get the plugin model table containing the list of workspace and + * external plug-ins + */ + InternationalizeModelTable pluginTable = runnable.getPluginTable(); + + if (!pluginTable.isEmpty()) { + + InternationalizeWizard wizard = new InternationalizeWizard(action, pluginTable); + wizard.init(PlatformUI.getWorkbench(), fSelection); + + //Create an operation to start and run the wizard + InternationalizeWizardOpenOperation op = new InternationalizeWizardOpenOperation(wizard); + try { + op.run(PDEPlugin.getActiveWorkbenchShell(), ""); //$NON-NLS-1$ + } catch (final InterruptedException irex) { + } + } else { + MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), PDEUIMessages.InternationalizeAction_internationalizeTitle, PDEUIMessages.InternationalizeAction_internationalizeMessage); + } + } + } + + public void selectionChanged(IAction action, ISelection selection) { + fSelection = (IStructuredSelection) selection; + } + + public void dispose() { + } + + public void init(IWorkbenchWindow window) { + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeModelTable.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeModelTable.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeModelTable.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeModelTable.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * Stores the list of BundlePluginModels and ExternalPluginModels to be passed to the + * InternationalizeWizard. This class could also used to populate the list of locales + * to which plug-ins will be internationalized. + * + * @author Team Azure + * + */ +public class InternationalizeModelTable { + private List fModels; + private List fPreSelected; //Models preselected by the user + + public InternationalizeModelTable() { + fModels = new ArrayList(); + fPreSelected = new ArrayList(); + } + + /** + * Adds the model to the model table. Takes into consideration the specified + * selection. + * @param model + * @param selected + */ + public void addToModelTable(Object model, boolean selected) { + if (selected) + fPreSelected.add(model); + else + fModels.add(model); + } + + /** + * Adds the model to the model table. + * @param model + */ + public void addModel(Object model) { + fModels.add(model); + } + + /** + * Removes the specified model from the model table. + * @param model + */ + public void removeModel(Object model) { + fModels.remove(model); + } + + /** + * + * @return the number of models in the table + */ + public int getModelCount() { + return fPreSelected.size() + fModels.size(); + } + + /** + * Returns the list of models stored in the model table + * @return the array of models + */ + public Object[] getModels() { + return fModels.toArray(); + } + + /** + * Returns the list of preselected models stored in the model table + * @return the array of preselected models + */ + public Object[] getPreSelected() { + return fPreSelected.toArray(); + } + + /** + * + * @return whether or not the model table contains preselected models + */ + public boolean hasPreSelected() { + return fPreSelected.size() > 0; + } + + /** + * + * @return whether or not the list of models is empty + */ + public boolean isEmpty() { + return fModels.size() == 0; + } +} Index: src/org/eclipse/pde/internal/ui/nls/AvailableFilter.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/AvailableFilter.java diff -N src/org/eclipse/pde/internal/ui/nls/AvailableFilter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/AvailableFilter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.Map; +import java.util.regex.Pattern; +import org.eclipse.jface.viewers.*; +import org.eclipse.pde.internal.core.util.PatternConstructor; + +public class AvailableFilter extends ViewerFilter { + public static final String WILDCARD = "*"; //$NON-NLS-1$ + private Pattern fPattern; + private final Map selected; + private final ILabelProvider labelProvider; + + public AvailableFilter(Map selected, ILabelProvider labelProvider) { + setPattern(WILDCARD); + this.selected = selected; + this.labelProvider = labelProvider; + } + + public boolean select(Viewer viewer, Object parentElement, Object element) { + // filter out any items that are currently selected + // on a full refresh, these will have been added back to the list + if (selected.containsKey(element)) + return false; + + String displayName = labelProvider.getText(element); + return matches(element.toString()) || matches(displayName); + } + + private boolean matches(String s) { + return fPattern.matcher(s.toLowerCase()).matches(); + } + + public boolean setPattern(String pattern) { + String newPattern = pattern.toLowerCase(); + + if (!newPattern.endsWith(WILDCARD)) + newPattern += WILDCARD; + if (!newPattern.startsWith(WILDCARD)) + newPattern = WILDCARD + newPattern; + if (fPattern != null) { + String oldPattern = fPattern.pattern(); + if (newPattern.equals(oldPattern)) + return false; + } + fPattern = PatternConstructor.createPattern(newPattern, true); + return true; + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardLocalePage.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardLocalePage.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardLocalePage.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardLocalePage.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,498 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.*; +import java.util.List; +import org.eclipse.core.runtime.*; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.*; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.IModelProviderEvent; +import org.eclipse.pde.core.IModelProviderListener; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.pde.internal.ui.elements.DefaultContentProvider; +import org.eclipse.pde.internal.ui.util.SWTUtil; +import org.eclipse.pde.internal.ui.wizards.ListUtil; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.*; +import org.eclipse.ui.progress.WorkbenchJob; + +public class InternationalizeWizardLocalePage extends InternationalizationWizardPage implements IModelProviderListener { + + public static final String PAGE_NAME = "InternationalizeWizardLocalePage"; //$NON-NLS-1$ + + protected Locale[] fModels = new Locale[0]; + private String fLocation; + + protected TableViewer fSelectedListViewer; + private boolean fRefreshNeeded = true; + + private Label fCountLabel; + private TableViewer fAvailableListViewer; + + // this job is used to delay the full filter refresh for 200 milliseconds in case the user is still typing + private WorkbenchJob fFilterJob; + private Text fFilterText; + private AvailableFilter fFilter; + + // fSelected is used to track the selection in a HashMap so we can efficiently + // filter selected items out of the available item list + private HashMap fSelected; + // used to block the selection listeners from updating button enablement when programatically removing items + private boolean fBlockSelectionListeners; + private Button fAddButton; + private Button fAddAllButton; + private Button fRemoveButton; + private Button fRemoveAllButton; + + private InternationalizeModelTable fInternationalizeModelTable; + + private class ContentProvider extends DefaultContentProvider implements IStructuredContentProvider { + public Object[] getElements(Object inputElement) { + return ((InternationalizeModelTable) inputElement).getModels(); + } + } + + public InternationalizeWizardLocalePage(InternationalizeModelTable modelTable, String pageName) { + super(pageName); + setTitle(PDEUIMessages.InternationalizeWizard_LocalePage_pageTitle); + setDescription(PDEUIMessages.InternationalizeWizard_LocalePage_pageDescription); + + PDEPlugin.getDefault().getLabelProvider().connect(this); + PDECore.getDefault().getModelManager().getExternalModelManager().addModelProviderListener(this); + + fInternationalizeModelTable = modelTable; + fSelected = new HashMap(); + + IWizardContainer container = getContainer(); + if (container != null) + container.updateButtons(); + } + + private void addFilter() { + fFilter = new AvailableFilter(fSelected, new LocaleLabelProvider()); + fAvailableListViewer.addFilter(fFilter); + fFilterJob = new WorkbenchJob("FilterJob") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + handleFilter(); + return Status.OK_STATUS; + } + }; + fFilterJob.setSystem(true); + } + + private void handleFilter() { + boolean changed = false; + String newFilter; + if (fFilterText == null || (newFilter = fFilterText.getText().trim()).length() == 0) + newFilter = AvailableFilter.WILDCARD; + changed = fFilter.setPattern(newFilter); + if (changed) { + fAvailableListViewer.getTable().setRedraw(false); + fAvailableListViewer.refresh(); + fAvailableListViewer.getTable().setRedraw(true); + updateButtonEnablement(false, false); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 3; + layout.makeColumnsEqualWidth = false; + layout.horizontalSpacing = 5; + layout.verticalSpacing = 20; + container.setLayout(layout); + + createScrollArea(container); + createAvailableList(container).setLayoutData(new GridData(GridData.FILL_BOTH)); + createButtonArea(container); + createLocaleList(container).setLayoutData(new GridData(GridData.FILL_BOTH)); + updateCount(); + + addViewerListeners(); + addFilter(); + + initialize(); + setControl(container); + Dialog.applyDialogFont(container); + } + + protected Composite createLocaleList(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + container.setLayout(layout); + container.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label label = new Label(container, SWT.NONE); + label.setText(PDEUIMessages.InternationalizeWizard_LocalePage_internationalizeList); + + Table table = new Table(container, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.widthHint = 225; + table.setLayoutData(gd); + + fSelectedListViewer = new TableViewer(table); + fSelectedListViewer.setLabelProvider(new LocaleLabelProvider()); + fSelectedListViewer.setContentProvider(new ContentProvider()); + fSelectedListViewer.setComparator(ListUtil.NAME_COMPARATOR); + return container; + } + + protected boolean isRefreshNeeded() { + if (fRefreshNeeded) { + fRefreshNeeded = false; + return true; + } + if (fLocation == null) { + return true; + } + return false; + } + + protected void addLocale(Locale model, ArrayList selected) { + if (!selected.contains(model)) { + selected.add(model); + } + } + + public List getLocalesForInternationalization() { + TableItem[] items = fSelectedListViewer.getTable().getItems(); + List result = new ArrayList(); + for (int i = 0; i < items.length; i++) { + result.add(items[i].getData()); + } + return result; + } + + public void storeSettings() { + IDialogSettings settings = getDialogSettings(); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.core.IModelProviderListener#modelsChanged(org.eclipse.pde.core.IModelProviderEvent) + */ + public void modelsChanged(IModelProviderEvent event) { + fRefreshNeeded = true; + } + + private void initialize() { + updateButtonEnablement(true, true); + setPageComplete(false); + } + + private void addViewerListeners() { + fAvailableListViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleAdd(); + } + }); + + fSelectedListViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleRemove(); + } + }); + + fAvailableListViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!fBlockSelectionListeners) + updateSelectionBasedEnablement(event.getSelection(), true); + } + }); + + fSelectedListViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!fBlockSelectionListeners) + updateSelectionBasedEnablement(event.getSelection(), false); + } + }); + + fFilterText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + fFilterJob.cancel(); + fFilterJob.schedule(200); + } + }); + + } + + private Composite createAvailableList(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + container.setLayout(layout); + container.setLayoutData(new GridData()); + + Label label = new Label(container, SWT.NONE); + label.setText(PDEUIMessages.InternationalizeWizard_LocalePage_availableList); + + Table table = new Table(container, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.widthHint = 225; + gd.heightHint = 400; + table.setLayoutData(gd); + + fAvailableListViewer = new TableViewer(table); + fAvailableListViewer.setLabelProvider(new LocaleLabelProvider()); + fAvailableListViewer.setContentProvider(new ContentProvider()); + fAvailableListViewer.setInput(fInternationalizeModelTable); + fAvailableListViewer.setComparator(ListUtil.NAME_COMPARATOR); + + return container; + } + + private Composite createButtonArea(Composite parent) { + ScrolledComposite comp = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); + GridLayout layout = new GridLayout(); + layout.marginWidth = layout.marginHeight = 0; + comp.setLayoutData(new GridData(GridData.FILL_VERTICAL)); + Composite container = new Composite(comp, SWT.NONE); + layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginTop = 50; + container.setLayout(layout); + GridData gd = new GridData(GridData.FILL_VERTICAL); + gd.verticalIndent = 15; + container.setLayoutData(gd); + + fAddButton = new Button(container, SWT.PUSH); + fAddButton.setText(PDEUIMessages.ImportWizard_DetailedPage_add); + fAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fAddButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleAdd(); + } + }); + SWTUtil.setButtonDimensionHint(fAddButton); + + fAddAllButton = new Button(container, SWT.PUSH); + fAddAllButton.setText(PDEUIMessages.ImportWizard_DetailedPage_addAll); + fAddAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fAddAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleAddAll(); + } + }); + SWTUtil.setButtonDimensionHint(fAddAllButton); + + fRemoveButton = new Button(container, SWT.PUSH); + fRemoveButton.setText(PDEUIMessages.ImportWizard_DetailedPage_remove); + fRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fRemoveButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleRemove(); + } + }); + SWTUtil.setButtonDimensionHint(fRemoveButton); + + fRemoveAllButton = new Button(container, SWT.PUSH); + fRemoveAllButton.setText(PDEUIMessages.ImportWizard_DetailedPage_removeAll); + fRemoveAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fRemoveAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleRemoveAll(); + } + }); + SWTUtil.setButtonDimensionHint(fRemoveAllButton); + + fCountLabel = new Label(container, SWT.NONE); + fCountLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER)); + comp.setContent(container); + comp.setMinHeight(250); + comp.setExpandHorizontal(true); + comp.setExpandVertical(true); + return container; + } + + private Composite createScrollArea(Composite parent) { + Group container = createFilterContainer(parent, PDEUIMessages.InternationalizeWizard_LocalePage_filter, PDEUIMessages.ImportWizard_DetailedPage_search); + fFilterText = createFilterText(container, ""); //$NON-NLS-1$ + return container; + } + + protected void refreshPage() { + fSelectedListViewer.getTable().removeAll(); + fSelected = new HashMap(); + fAvailableListViewer.refresh(); + pageChanged(); + } + + protected void pageChanged() { + pageChanged(false, false); + } + + protected void pageChanged(boolean doAddEnablement, boolean doRemoveEnablement) { + if (fSelectedListViewer.getTable().getItemCount() == 0) { + setErrorMessage(PDEUIMessages.InternationalizeWizard_LocalePage_selectionError); + } else { + setErrorMessage(null); + } + + updateCount(); + updateButtonEnablement(doAddEnablement, doRemoveEnablement); + setPageComplete(fSelectedListViewer.getTable().getItemCount() > 0); + } + + private void updateCount() { + fCountLabel.setText(NLS.bind(PDEUIMessages.ImportWizard_DetailedPage_count, (new String[] {new Integer(fSelectedListViewer.getTable().getItemCount()).toString(), new Integer(fAvailableListViewer.getTable().getItemCount() + fSelectedListViewer.getTable().getItemCount()).toString()}))); + fCountLabel.getParent().layout(); + } + + private void updateButtonEnablement(boolean doAddEnablement, boolean doRemoveEnablement) { + int availableCount = fAvailableListViewer.getTable().getItemCount(); + int importCount = fSelectedListViewer.getTable().getItemCount(); + + if (doAddEnablement) + updateSelectionBasedEnablement(fAvailableListViewer.getSelection(), true); + if (doRemoveEnablement) + updateSelectionBasedEnablement(fSelectedListViewer.getSelection(), false); + + fAddAllButton.setEnabled(availableCount > 0); + fRemoveAllButton.setEnabled(importCount > 0); + } + + private void updateSelectionBasedEnablement(ISelection theSelection, boolean available) { + if (available) + fAddButton.setEnabled(!theSelection.isEmpty()); + else + fRemoveButton.setEnabled(!theSelection.isEmpty()); + } + + private void handleAdd() { + IStructuredSelection ssel = (IStructuredSelection) fAvailableListViewer.getSelection(); + if (ssel.size() > 0) { + Table table = fAvailableListViewer.getTable(); + int index = table.getSelectionIndices()[0]; + Object[] selection = ssel.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < selection.length; i++) { + doAdd(selection[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + table.setSelection(index < table.getItemCount() ? index : table.getItemCount() - 1); + pageChanged(true, false); + } + } + + private void handleAddAll() { + TableItem[] items = fAvailableListViewer.getTable().getItems(); + + ArrayList data = new ArrayList(); + for (int i = 0; i < items.length; i++) { + data.add(items[i].getData()); + } + if (data.size() > 0) { + Object[] datas = data.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < datas.length; i++) { + doAdd(datas[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + pageChanged(true, false); + } + } + + private void handleRemove() { + IStructuredSelection ssel = (IStructuredSelection) fSelectedListViewer.getSelection(); + if (ssel.size() > 0) { + Table table = fSelectedListViewer.getTable(); + int index = table.getSelectionIndices()[0]; + Object[] selection = ssel.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < selection.length; i++) { + doRemove(selection[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + table.setSelection(index < table.getItemCount() ? index : table.getItemCount() - 1); + pageChanged(false, true); + } + } + + private void doAdd(Object o) { + fInternationalizeModelTable.removeModel(o); + fSelectedListViewer.add(o); + fAvailableListViewer.remove(o); + fSelected.put(o, null); + } + + private void doRemove(Object o) { + fInternationalizeModelTable.addModel(o); + fSelected.remove(o); + fSelectedListViewer.remove(o); + fAvailableListViewer.add(o); + } + + // used to prevent flicker during operations that move items between lists + private void setRedraw(boolean redraw) { + fAvailableListViewer.getTable().setRedraw(redraw); + fSelectedListViewer.getTable().setRedraw(redraw); + } + + private void handleRemoveAll() { + TableItem[] items = fSelectedListViewer.getTable().getItems(); + + ArrayList data = new ArrayList(); + for (int i = 0; i < items.length; i++) { + data.add(items[i].getData()); + } + if (data.size() > 0) { + Object[] datas = data.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < datas.length; i++) { + doRemove(datas[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + pageChanged(false, true); + } + } + + public void dispose() { + PDEPlugin.getDefault().getLabelProvider().disconnect(this); + PDECore.getDefault().getModelManager().getExternalModelManager().removeModelProviderListener(this); + } + + private void setBlockSelectionListeners(boolean blockSelectionListeners) { + fBlockSelectionListeners = blockSelectionListeners; + } + + public boolean isCurrentPage() { + return super.isCurrentPage(); + } + + public boolean canFlipToNextPage() { + return false; + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizard.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizard.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeWizard.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeWizard.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.*; +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.internal.core.plugin.ExternalPluginModel; +import org.eclipse.pde.internal.ui.*; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.IWorkbench; + +/** + * An InternationalizeWizard is responsible for internationalizing a list of + * specified plug-ins (workspace and external) to a set of specified locales. + * This involves creating an NLS fragment project for every plug-in, which would + * contain properties files for each specified locale. The first page of the wizard + * allows the user to select the desired plug-ins and the second page the desired + * locales. Also, the wizard ensures the plug-ins are externalized before proceeding + * with internationlization. + * + * @author Team Azure + * + */ +public class InternationalizeWizard extends Wizard implements IImportWizard { + private static final String STORE_SECTION = "InternationalizeWizard"; //$NON-NLS-1$ + + private IAction action; + private IStructuredSelection selection; + + //An intermediate selection passed to the ExternalizeStringsWizard + private IStructuredSelection externalizeSelection; + + private InternationalizeWizardPluginPage page1; + private InternationalizeWizardLocalePage page2; + + //Contains the list of plug-ins to be internationalized + private InternationalizeModelTable fInternationalizePluginModelTable; + + //Contains the list of locales + private InternationalizeModelTable fInternationalizeLocaleModelTable; + + public InternationalizeWizard(IAction action, InternationalizeModelTable pluginTable) { + fInternationalizePluginModelTable = pluginTable; + populateLocaleModelTable(); + IDialogSettings masterSettings = PDEPlugin.getDefault().getDialogSettings(); + setDialogSettings(getSettingsSection(masterSettings)); + setDefaultPageImageDescriptor(PDEPluginImages.DESC_XHTML_CONVERT_WIZ); + setWindowTitle(PDEUIMessages.InternationalizeWizard_title); + this.action = action; + setHelpAvailable(false); + } + + /** + * Populates the local InternationalizeModelTable with the list of all + * available locales + */ + private void populateLocaleModelTable() { + fInternationalizeLocaleModelTable = new InternationalizeModelTable(); + Locale[] availableLocales = Locale.getAvailableLocales(); + for (int i = 0; i < availableLocales.length; i++) { + fInternationalizeLocaleModelTable.addModel(availableLocales[i]); + } + } + + /** + * Initialises selections + */ + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + externalizeSelection = this.selection; + } + + /** + * Adds the plug-in and locale pages to the wizard + */ + public void addPages() { + setNeedsProgressMonitor(true); + page1 = new InternationalizeWizardPluginPage(fInternationalizePluginModelTable, "Plug-ins"); //$NON-NLS-1$ + addPage(page1); + + page2 = new InternationalizeWizardLocalePage(fInternationalizeLocaleModelTable, "Locales"); //$NON-NLS-1$ + addPage(page2); + } + + /** + * + * @param master + * @return the created setting for the InternationalizeWizard + */ + private IDialogSettings getSettingsSection(IDialogSettings master) { + IDialogSettings setting = master.getSection(STORE_SECTION); + if (setting == null) { + setting = master.addNewSection(STORE_SECTION); + } + return setting; + } + + /** + * + * @return the list of plug-ins selected for internationalization + */ + private List getPluginModelsForInternationalization() { + return page1.getModelsToInternationalize(); + } + + /** + * + * @return the list of locales specified for internationalization + */ + private List getLocalesForInternationalization() { + return page2.getLocalesForInternationalization(); + } + + public boolean performFinish() { + page1.storeSettings(); + page2.storeSettings(); + + //Generate an NL fragment project for each of the selected plug-ins with the specified locales + String template = page1.getTemplate() + (page1.createIndividualFragments() ? "." + NLSFragmentGenerator.LOCALE_NAME_MACRO : ""); //$NON-NLS-1$ //$NON-NLS-2$ + NLSFragmentGenerator fragmentGenerator = new NLSFragmentGenerator(template, getPluginModelsForInternationalization(), getLocalesForInternationalization(), this.getContainer(), page1.overwriteWithoutAsking()); + return fragmentGenerator.generate(); + } + + /** + * + * @param currentPage + * @return the next wizard page + */ + public IWizardPage getNextPage(IWizardPage currentPage) { + if (currentPage.equals(page1)) { + ensurePluginsAreExternalized(); + return page2; + } + return null; + } + + /** + * + * @param currentPage + * @return the previous wizard page + */ + public IWizardPage getPreviousPage(IWizardPage currentPage) { + if (currentPage.equals(page2)) { + return page1; + } + return null; + } + + public boolean canFinish() { + return getPluginModelsForInternationalization().size() > 0 && getLocalesForInternationalization().size() > 0; + } + + /** + * Checks whether or not the selected plug-ins are already externalized. This + * method invokes the ExternalizeStringsWizard on the selected plug-ins. + */ + public void ensurePluginsAreExternalized() { + GetNonExternalizedStringsAction externalize = new GetNonExternalizedStringsAction(); + + List projects = new ArrayList(); + List pluginModels = getPluginModelsForInternationalization(); + selection = new StructuredSelection(pluginModels); //Save the plug-ins selected for internationalization in a StructuredSelection + + for (Iterator it = pluginModels.iterator(); it.hasNext();) { + IPluginModelBase pluginModel = (IPluginModelBase) it.next(); + //Externalize only workspace plug-ins since external plug-ins are already externalized + if (!(pluginModel instanceof ExternalPluginModel)) { + IProject project = pluginModel.getUnderlyingResource().getProject(); + projects.add(project); + } + } + + //Set the selection for the non-externalized plug-ins that + //should be passed to the ExternalizeStringsWizard + externalizeSelection = new StructuredSelection(projects); + + externalize.selectionChanged(action, externalizeSelection); + externalize.setExternalizeSelectedPluginsOnly(true); + externalize.setSkipMessageDialog(true); + externalize.run(action); + } + + public boolean performCancel() { + return super.performCancel(); + } + + public boolean isHelpAvailable() { + return false; + } +} Index: src/org/eclipse/pde/internal/ui/nls/LocaleLabelProvider.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/LocaleLabelProvider.java diff -N src/org/eclipse/pde/internal/ui/nls/LocaleLabelProvider.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/LocaleLabelProvider.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.Locale; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.pde.internal.ui.PDEPluginImages; +import org.eclipse.swt.graphics.Image; + +public class LocaleLabelProvider implements ILabelProvider { + + public Image getImage(Object element) { + return PDEPluginImages.DESC_DISCOVERY.createImage(); + } + + public String getText(Object element) { + Locale locale = (Locale) element; + String country = " (" + locale.getDisplayCountry() + ")"; + return locale.getDisplayLanguage() + (" ()".equals(country) ? "" : country); + } + + public void addListener(ILabelProviderListener listener) { + // TODO Auto-generated method stub + + } + + public void dispose() { + // TODO Auto-generated method stub + } + + public boolean isLabelProperty(Object element, String property) { + // TODO Auto-generated method stub + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // TODO Auto-generated method stub + + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeOperation.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeOperation.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeOperation.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeOperation.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; +import org.eclipse.pde.internal.core.WorkspaceModelManager; +import org.eclipse.pde.internal.ui.PDEUIMessages; + +/** + * InternationalizeOperation is responsible for populating a plug-in model table + * containing the list of plug-ins (workspace and external) prior to running the + * wizard. An instance of this class must be created before creating an + * InternationlizeWizard instance. + * + * @author Team Azure + * + */ +public class InternationalizeOperation implements IRunnableWithProgress { + + private ISelection fSelection; + private ArrayList fSelectedModels; + private InternationalizeModelTable fModelPluginTable; + private boolean fCanceled; + + /** + * + * @param selection represents the preselected plug-in projects in the workbench + */ + public InternationalizeOperation(ISelection selection) { + fSelection = selection; + } + + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + + if (fSelection instanceof IStructuredSelection) { + + Object[] elems = ((IStructuredSelection) fSelection).toArray(); + + fSelectedModels = new ArrayList(elems.length); + for (int i = 0; i < elems.length; i++) { + //If a file was selected, get its parent project + if (elems[i] instanceof IFile) + elems[i] = ((IFile) elems[i]).getProject(); + + //Add the project to the preselected model list + if (elems[i] instanceof IProject && WorkspaceModelManager.isPluginProject((IProject) elems[i]) && !WorkspaceModelManager.isBinaryProject((IProject) elems[i])) + fSelectedModels.add(elems[i]); + } + } + + //Get all models (workspace and external) excluding fragment models + IPluginModelBase[] pluginModels = PluginRegistry.getAllModels(false); + monitor.beginTask(PDEUIMessages.GetNonExternalizedStringsOperation_taskMessage, pluginModels.length); + + //Populate list to an InternationalizeModelTable + fModelPluginTable = new InternationalizeModelTable(); + for (int i = 0; i < pluginModels.length; i++) { + fModelPluginTable.addToModelTable(pluginModels[i], pluginModels[i].getUnderlyingResource() != null ? selected(pluginModels[i].getUnderlyingResource().getProject()) : false); + } + } + + /** + * + * @return whether or not the operation was cancelled + */ + public boolean wasCanceled() { + return fCanceled; + } + + /** + * + * @param project + * @return whether or not the project was preselected + */ + public boolean selected(IProject project) { + return fSelectedModels.contains(project); + } + + /** + * + * @return the InternationalizeModelTable containing the plug-ins + */ + public InternationalizeModelTable getPluginTable() { + return fModelPluginTable; + } +} Index: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardPluginPage.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardPluginPage.java diff -N src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardPluginPage.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/nls/InternationalizeWizardPluginPage.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,645 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.pde.internal.ui.nls; + +import java.util.*; +import java.util.List; +import org.eclipse.core.runtime.*; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.*; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.IModelProviderEvent; +import org.eclipse.pde.core.IModelProviderListener; +import org.eclipse.pde.core.plugin.*; +import org.eclipse.pde.internal.core.ClasspathUtilCore; +import org.eclipse.pde.internal.core.PDECore; +import org.eclipse.pde.internal.ui.PDEPlugin; +import org.eclipse.pde.internal.ui.PDEUIMessages; +import org.eclipse.pde.internal.ui.elements.DefaultContentProvider; +import org.eclipse.pde.internal.ui.util.SWTUtil; +import org.eclipse.pde.internal.ui.wizards.ListUtil; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.*; +import org.eclipse.ui.progress.WorkbenchJob; + +/** + * The first page of the InternationalizeWizard. This page allows the user to + * select the desired plug-ins for internationalization. These could be plug-ins + * in the user's workspace or external ones. + * + * @author Team Azure + * + */ +public class InternationalizeWizardPluginPage extends InternationalizationWizardPage implements IModelProviderListener { + + private static final String CREATE_INDIVIDUAL_FRAGMENTS = "create individual fragments"; + private static final String TEMPLATE = "name_template"; //$NON-NLS-1$ + private static final String OVERWRITE = "overwrite?"; //$NON-NLS-1$ + public static final String PAGE_NAME = "InternationalizeWizardPluginPage"; //$NON-NLS-1$ + + protected IPluginModelBase[] fModels = new IPluginModelBase[0]; + + private boolean fRefreshNeeded = true; + + private Label fCountLabel; //Displays "x out of y selected" + + private TableViewer fAvailableViewer; //All available plug-ins + protected TableViewer fSelectedViewer; //Selected plug-ins + + private WorkbenchJob fFilterJob; + private Text fFilterText; + private Text fTemplateText; + private AvailableFilter fFilter; + + // Used to track the selection in a HashMap so as to filter + // selected items out of the available item list + private final Map fSelected = new HashMap(); + + // Used to block the selection listeners from updating button enablement + // when programatically removing items + private boolean fBlockSelectionListeners; + private Button fAddButton; + private Button fAddAllButton; + private Button fRemoveButton; + private Button fRemoveAllButton; + + // Used to store the plug-ins + private InternationalizeModelTable fInternationalizeModelTable; + + private Button overwriteOption; + private Button individualFragments; + + private class ContentProvider extends DefaultContentProvider implements IStructuredContentProvider { + /** + * @return the list of available non-selected plug-ins + */ + public Object[] getElements(Object parent) { + return fInternationalizeModelTable.getModels(); + } + } + + private class SelectedContentProvider extends DefaultContentProvider implements IStructuredContentProvider { + /** + * @return the list of selected plug-ins + */ + public Object[] getElements(Object parent) { + return fInternationalizeModelTable.getPreSelected(); + } + } + + public InternationalizeWizardPluginPage(InternationalizeModelTable modelTable, String pageName) { + + super(pageName); + setTitle(PDEUIMessages.InternationalizeWizard_PluginPage_pageTitle); + setDescription(PDEUIMessages.InternationalizeWizard_PluginPage_pageDescription); + + PDEPlugin.getDefault().getLabelProvider().connect(this); + PDECore.getDefault().getModelManager().getExternalModelManager().addModelProviderListener(this); + + fInternationalizeModelTable = modelTable; + + IWizardContainer container = getContainer(); + if (container != null) + container.updateButtons(); + } + + /** + * Adds a filter to the list of available plug-ins + */ + private void addFilter() { + fFilter = new AvailableFilter(fSelected, PDEPlugin.getDefault().getLabelProvider()); + fAvailableViewer.addFilter(fFilter); + fFilterJob = new WorkbenchJob("FilterJob") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + handleFilter(); + return Status.OK_STATUS; + } + }; + fFilterJob.setSystem(true); + } + + /** + * Handles changes to the list based on changes to the text field. + */ + private void handleFilter() { + boolean changed = false; + String newFilter; + if (fFilterText == null || (newFilter = fFilterText.getText().trim()).length() == 0) + newFilter = AvailableFilter.WILDCARD; + changed = fFilter.setPattern(newFilter); + if (changed) { + fAvailableViewer.getTable().setRedraw(false); + fAvailableViewer.refresh(); + fAvailableViewer.getTable().setRedraw(true); + updateButtonEnablement(false, false); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 3; + layout.makeColumnsEqualWidth = false; + layout.horizontalSpacing = 5; + layout.verticalSpacing = 20; + container.setLayout(layout); + + createScrollArea(container); + createAvailableList(container).setLayoutData(new GridData(GridData.FILL_BOTH)); + createButtonArea(container); + createInternationalizeList(container).setLayoutData(new GridData(GridData.FILL_BOTH)); + updateCount(); + + GridData data = new GridData(GridData.FILL_HORIZONTAL); + data.horizontalSpan = 3; + Composite comp = new Composite(container, SWT.NONE); + comp.setLayoutData(data); + GridLayout fl = new GridLayout(2, false); + comp.setLayout(fl); + + IDialogSettings settings = getDialogSettings(); + String template = settings.get(TEMPLATE); + + Label label = new Label(comp, SWT.NONE); + label.setText(PDEUIMessages.InternationalizeWizard_PluginPage_templateLabel); + fTemplateText = new Text(comp, SWT.BORDER); + fTemplateText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fTemplateText.setText(template != null ? template : NLSFragmentGenerator.PLUGIN_NAME_MACRO + ".nl1"); //$NON-NLS-1$ + fTemplateText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + pageChanged(); + } + }); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + + overwriteOption = new Button(comp, SWT.CHECK); + overwriteOption.setText(PDEUIMessages.InternationalizeWizard_PluginPage_overwriteWithoutAsking); + overwriteOption.setSelection(settings.getBoolean(OVERWRITE)); + overwriteOption.setLayoutData(gd); + + individualFragments = new Button(comp, SWT.CHECK); + individualFragments.setText(PDEUIMessages.InternationalizeWizard_PluginPage_individualFragments); + individualFragments.setSelection(settings.getBoolean(CREATE_INDIVIDUAL_FRAGMENTS)); + individualFragments.setLayoutData(gd); + + addViewerListeners(); + addFilter(); + + initialize(); + setControl(container); + Dialog.applyDialogFont(container); + } + + /** + * @param parent + * @return the container holding the available plug-ins list + */ + private Composite createAvailableList(Composite parent) { + Composite container = createViewerContainer(parent, PDEUIMessages.InternationalizeWizard_PluginPage_availableList); + fAvailableViewer = createTableViewer(container, new ContentProvider(), PDECore.getDefault().getModelManager()); + return container; + } + + protected Composite createInternationalizeList(Composite parent) { + Composite container = createViewerContainer(parent, PDEUIMessages.InternationalizeWizard_PluginPage_internationalizeList); + fSelectedViewer = createTableViewer(container, new SelectedContentProvider(), PDECore.getDefault().getModelManager().getExternalModelManager()); + return container; + } + + private Composite createViewerContainer(Composite parent, String message) { + Composite container = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + container.setLayout(layout); + container.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label label = new Label(container, SWT.NONE); + label.setText(message); + return container; + } + + private static TableViewer createTableViewer(Composite container, IContentProvider provider, Object manager) { + Table table = new Table(container, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.heightHint = 200; + gd.widthHint = 225; + table.setLayoutData(gd); + + TableViewer viewer = new TableViewer(table); + viewer.setLabelProvider(PDEPlugin.getDefault().getLabelProvider()); + viewer.setContentProvider(provider); + viewer.setInput(manager); + viewer.setComparator(ListUtil.PLUGIN_COMPARATOR); + return viewer; + } + + protected boolean isRefreshNeeded() { + if (fRefreshNeeded) { + fRefreshNeeded = false; + return true; + } + + return false; + } + + private IPluginModelBase findModel(String id) { + for (int i = 0; i < fModels.length; i++) { + String modelId = fModels[i].getPluginBase().getId(); + if (modelId != null && modelId.equals(id)) + return fModels[i]; + } + return null; + } + + private IFragmentModel[] findFragments(IPlugin plugin) { + ArrayList result = new ArrayList(); + for (int i = 0; i < fModels.length; i++) { + if (fModels[i] instanceof IFragmentModel) { + IFragment fragment = ((IFragmentModel) fModels[i]).getFragment(); + if (plugin.getId().equalsIgnoreCase(fragment.getPluginId())) { + result.add(fModels[i]); + } + } + } + return (IFragmentModel[]) result.toArray(new IFragmentModel[result.size()]); + } + + protected void addPluginAndDependencies(IPluginModelBase model, ArrayList selected, boolean addFragments) { + + boolean containsVariable = false; + if (!selected.contains(model)) { + selected.add(model); + boolean hasextensibleAPI = ClasspathUtilCore.hasExtensibleAPI(model); + if (!addFragments && !hasextensibleAPI && model instanceof IPluginModel) { + IPluginLibrary[] libraries = model.getPluginBase().getLibraries(); + for (int i = 0; i < libraries.length; i++) { + if (ClasspathUtilCore.containsVariables(libraries[i].getName())) { + containsVariable = true; + break; + } + } + } + addDependencies(model, selected, addFragments || containsVariable || hasextensibleAPI); + } + } + + protected void addDependencies(IPluginModelBase model, ArrayList selected, boolean addFragments) { + + IPluginImport[] required = model.getPluginBase().getImports(); + if (required.length > 0) { + for (int i = 0; i < required.length; i++) { + IPluginModelBase found = findModel(required[i].getId()); + if (found != null) { + addPluginAndDependencies(found, selected, addFragments); + } + } + } + + if (addFragments) { + if (model instanceof IPluginModel) { + IFragmentModel[] fragments = findFragments(((IPluginModel) model).getPlugin()); + for (int i = 0; i < fragments.length; i++) { + addPluginAndDependencies(fragments[i], selected, addFragments); + } + } else { + IFragment fragment = ((IFragmentModel) model).getFragment(); + IPluginModelBase found = findModel(fragment.getPluginId()); + if (found != null) { + addPluginAndDependencies(found, selected, addFragments); + } + } + } + } + + public List getModelsToInternationalize() { + TableItem[] items = fSelectedViewer.getTable().getItems(); + List result = new ArrayList(); + for (int i = 0; i < items.length; i++) { + result.add(items[i].getData()); + } + return result; + } + + public void storeSettings() { + IDialogSettings settings = getDialogSettings(); + settings.put(OVERWRITE, overwriteWithoutAsking()); + settings.put(TEMPLATE, getTemplate()); + settings.put(CREATE_INDIVIDUAL_FRAGMENTS, createIndividualFragments()); + } + + public boolean createIndividualFragments() { + return individualFragments.getSelection(); + } + + /* (non-Javadoc) + * @see org.eclipse.pde.core.IModelProviderListener#modelsChanged(org.eclipse.pde.core.IModelProviderEvent) + */ + public void modelsChanged(IModelProviderEvent event) { + fRefreshNeeded = true; + } + + private void initialize() { + updateButtonEnablement(true, true); + setPageComplete(false); + } + + private void addViewerListeners() { + fAvailableViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleAdd(); + } + }); + + fSelectedViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleRemove(); + } + }); + + fAvailableViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!fBlockSelectionListeners) + updateSelectionBasedEnablement(event.getSelection(), true); + } + }); + + fSelectedViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!fBlockSelectionListeners) + updateSelectionBasedEnablement(event.getSelection(), false); + } + }); + + fFilterText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + fFilterJob.cancel(); + fFilterJob.schedule(200); + } + }); + + } + + private Composite createButtonArea(Composite parent) { + ScrolledComposite comp = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); + GridLayout layout = new GridLayout(); + layout.marginWidth = layout.marginHeight = 0; + comp.setLayoutData(new GridData(GridData.FILL_VERTICAL)); + Composite container = new Composite(comp, SWT.NONE); + layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginTop = 50; + container.setLayout(layout); + GridData gd = new GridData(GridData.FILL_VERTICAL); + gd.verticalIndent = 15; + container.setLayoutData(gd); + + fAddButton = new Button(container, SWT.PUSH); + fAddButton.setText(PDEUIMessages.ImportWizard_DetailedPage_add); + fAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fAddButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleAdd(); + } + }); + SWTUtil.setButtonDimensionHint(fAddButton); + + fAddAllButton = new Button(container, SWT.PUSH); + fAddAllButton.setText(PDEUIMessages.ImportWizard_DetailedPage_addAll); + fAddAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fAddAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleAddAll(); + } + }); + SWTUtil.setButtonDimensionHint(fAddAllButton); + + fRemoveButton = new Button(container, SWT.PUSH); + fRemoveButton.setText(PDEUIMessages.ImportWizard_DetailedPage_remove); + fRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fRemoveButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleRemove(); + } + }); + SWTUtil.setButtonDimensionHint(fRemoveButton); + + fRemoveAllButton = new Button(container, SWT.PUSH); + fRemoveAllButton.setText(PDEUIMessages.ImportWizard_DetailedPage_removeAll); + fRemoveAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fRemoveAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleRemoveAll(); + } + }); + SWTUtil.setButtonDimensionHint(fRemoveAllButton); + + fCountLabel = new Label(container, SWT.NONE); + fCountLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER)); + comp.setContent(container); + comp.setMinHeight(250); + comp.setExpandHorizontal(true); + comp.setExpandVertical(true); + return container; + } + + private Composite createScrollArea(Composite parent) { + Group container = createFilterContainer(parent, PDEUIMessages.InternationalizeWizard_PluginPage_filter, PDEUIMessages.ImportWizard_DetailedPage_search); + fFilterText = createFilterText(container, ""); //$NON-NLS-1$ + return container; + } + + protected void refreshPage() { + fSelectedViewer.getTable().removeAll(); + fSelected.clear(); + fAvailableViewer.refresh(); + pageChanged(); + } + + protected void pageChanged() { + pageChanged(false, false); + } + + protected void pageChanged(boolean doAddEnablement, boolean doRemoveEnablement) { + if (fTemplateText.getText().length() == 0) { + setErrorMessage(PDEUIMessages.InternationalizeWizard_PluginPage_templateError); + } else if (fSelectedViewer.getTable().getItemCount() == 0) { + setErrorMessage(PDEUIMessages.InternationalizeWizard_PluginPage_selectionError); + } else { + setErrorMessage(null); + } + + updateCount(); + updateButtonEnablement(doAddEnablement, doRemoveEnablement); + setPageComplete(fSelectedViewer.getTable().getItemCount() > 0); + } + + private void updateCount() { + fCountLabel.setText(NLS.bind(PDEUIMessages.ImportWizard_DetailedPage_count, (new String[] {new Integer(fSelectedViewer.getTable().getItemCount()).toString(), new Integer(fAvailableViewer.getTable().getItemCount() + fSelectedViewer.getTable().getItemCount()).toString()}))); + fCountLabel.getParent().layout(); + } + + private void updateButtonEnablement(boolean doAddEnablement, boolean doRemoveEnablement) { + int availableCount = fAvailableViewer.getTable().getItemCount(); + int importCount = fSelectedViewer.getTable().getItemCount(); + + if (doAddEnablement) + updateSelectionBasedEnablement(fAvailableViewer.getSelection(), true); + if (doRemoveEnablement) + updateSelectionBasedEnablement(fSelectedViewer.getSelection(), false); + + fAddAllButton.setEnabled(availableCount > 0); + fRemoveAllButton.setEnabled(importCount > 0); + } + + private void updateSelectionBasedEnablement(ISelection theSelection, boolean available) { + if (available) + fAddButton.setEnabled(!theSelection.isEmpty()); + else + fRemoveButton.setEnabled(!theSelection.isEmpty()); + } + + private void handleAdd() { + IStructuredSelection ssel = (IStructuredSelection) fAvailableViewer.getSelection(); + if (ssel.size() > 0) { + Table table = fAvailableViewer.getTable(); + int index = table.getSelectionIndices()[0]; + Object[] selection = ssel.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < selection.length; i++) { + doAdd(selection[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + table.setSelection(index < table.getItemCount() ? index : table.getItemCount() - 1); + pageChanged(true, false); + } + } + + private void handleAddAll() { + TableItem[] items = fAvailableViewer.getTable().getItems(); + + ArrayList data = new ArrayList(); + for (int i = 0; i < items.length; i++) { + data.add(items[i].getData()); + } + if (data.size() > 0) { + Object[] datas = data.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < datas.length; i++) { + doAdd(datas[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + pageChanged(true, false); + } + } + + private void handleRemove() { + IStructuredSelection ssel = (IStructuredSelection) fSelectedViewer.getSelection(); + if (ssel.size() > 0) { + Table table = fSelectedViewer.getTable(); + int index = table.getSelectionIndices()[0]; + Object[] selection = ssel.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < selection.length; i++) { + doRemove(selection[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + table.setSelection(index < table.getItemCount() ? index : table.getItemCount() - 1); + pageChanged(false, true); + } + } + + private void doAdd(Object o) { + fInternationalizeModelTable.removeModel(o); + fSelectedViewer.add(o); + fAvailableViewer.remove(o); + fSelected.put(o, null); + } + + private void doRemove(Object o) { + fInternationalizeModelTable.addModel(o); + fSelected.remove(o); + fSelectedViewer.remove(o); + fAvailableViewer.add(o); + } + + // used to prevent flicker during operations that move items between lists + private void setRedraw(boolean redraw) { + fAvailableViewer.getTable().setRedraw(redraw); + fSelectedViewer.getTable().setRedraw(redraw); + } + + private void handleRemoveAll() { + TableItem[] items = fSelectedViewer.getTable().getItems(); + + ArrayList data = new ArrayList(); + for (int i = 0; i < items.length; i++) { + data.add(items[i].getData()); + } + if (data.size() > 0) { + Object[] datas = data.toArray(); + setBlockSelectionListeners(true); + setRedraw(false); + for (int i = 0; i < datas.length; i++) { + doRemove(datas[i]); + } + setRedraw(true); + setBlockSelectionListeners(false); + pageChanged(false, true); + } + } + + public void dispose() { + PDEPlugin.getDefault().getLabelProvider().disconnect(this); + PDECore.getDefault().getModelManager().getExternalModelManager().removeModelProviderListener(this); + } + + private void setBlockSelectionListeners(boolean blockSelectionListeners) { + fBlockSelectionListeners = blockSelectionListeners; + } + + public boolean isCurrentPage() { + return super.isCurrentPage(); + } + + public boolean canFlipToNextPage() { + if (fSelectedViewer.getTable().getItems().length > 0 && getTemplate().length() > 0) { + return true; + } + return false; + } + + public String getTemplate() { + return fTemplateText.getText(); + } + + public boolean overwriteWithoutAsking() { + return overwriteOption.getSelection(); + } +}