diff options
Diffstat (limited to 'org.eclipse.m2e.core/src/org/eclipse/m2e/core')
287 files changed, 45186 insertions, 0 deletions
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenImages.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenImages.java new file mode 100644 index 00000000..2c58294d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenImages.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.jface.viewers.DecorationOverlayIcon; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * @author Eugene Kuleshov + */ +public class MavenImages { + + // object images + + public static final Image IMG_CLEAR = createImage("clear.gif"); //$NON-NLS-1$ + + public static final Image IMG_CLEAR_DISABLED = createImage("clear_disabled.gif"); //$NON-NLS-1$ + + public static final String PATH_JAR = "jar_obj.gif"; //$NON-NLS-1$ + + public static final Image IMG_JAR = createImage(PATH_JAR); + + public static final String PATH_LOCK = "lock_ovr.gif"; //$NON-NLS-1$ + + public static final String PATH_VERSION = "jar_version.gif"; //$NON-NLS-1$ + + public static final Image IMG_VERSION = createImage(PATH_VERSION); + + public static final String PATH_VERSION_SRC = "jar_src_version.gif"; //$NON-NLS-1$ + + public static final Image IMG_VERSION_SRC = createImage(PATH_VERSION_SRC); + + public static final Image IMG_JAVA = createImage("java_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_JAVA_SRC = createImage("java_src_obj.gif"); //$NON-NLS-1$ + + // public static final Image IMG_M2 = createImage("m2.gif"); + + public static final Image IMG_LAUNCH_MAIN = createImage("main_tab.gif"); //$NON-NLS-1$ + + public static final Image IMG_INDEX = createImage("maven_index.gif"); //$NON-NLS-1$ + + public static final Image IMG_INDEXES = createImage("maven_indexes.gif"); //$NON-NLS-1$ + + public static final Image IMG_MAVEN_JAR = createImage("mjar.gif"); //$NON-NLS-1$ + + // public static final Image IMG_JAR = createImage("mlabel.gif"); + + public static final Image IMG_NEW_POM = createImage("new_m2_pom.gif"); //$NON-NLS-1$ + + public static final Image IMG_NEW_PROJECT = createImage("new_m2_project.gif"); //$NON-NLS-1$ + + public static final Image IMG_OPEN_POM = createImage("open_pom.gif"); //$NON-NLS-1$ + + // public static final Image IMG_POM = createImage("pom_obj.gif"); + + public static final Image IMG_UPD_DEPENDENCIES = createImage("update_dependencies.gif"); //$NON-NLS-1$ + + public static final Image IMG_UPD_SOURCES = createImage("update_source_folders.gif"); //$NON-NLS-1$ + + public static final Image IMG_WEB = createImage("web.gif"); //$NON-NLS-1$ + + // wizard images + + public static final ImageDescriptor WIZ_IMPORT_WIZ = createDescriptor("import_project.png"); //$NON-NLS-1$ + + public static final ImageDescriptor WIZ_NEW_PROJECT = createDescriptor("new_m2_project_wizard.gif"); //$NON-NLS-1$ + + // descriptors + + public static final ImageDescriptor M2 = createDescriptor("m2.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor DEBUG = createDescriptor("debug.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor ADD_INDEX = createDescriptor("add_index.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor CLOSE = createDescriptor("close.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor COPY = createDescriptor("copy.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor COLLAPSE_ALL = createDescriptor("collapseall.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor EXPAND_ALL = createDescriptor("expandall.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor NEW_POM = createDescriptor("new_m2_pom.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor REFRESH = createDescriptor("refresh.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor UPD_INDEX = createDescriptor("update_index.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor REBUILD_INDEX = createDescriptor("rebuild_index.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor POM = createDescriptor("pom_obj.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor IMPORT_PROJECT = createDescriptor("import_m2_project.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor SHOW_CONSOLE_ERR = createDescriptor("stderr.gif"); //$NON-NLS-1$ + + public static final ImageDescriptor SHOW_CONSOLE_OUT = createDescriptor("stdout.gif"); //$NON-NLS-1$ + + private static ImageDescriptor createDescriptor(String key) { + try { + ImageRegistry imageRegistry = getImageRegistry(); + if(imageRegistry != null) { + ImageDescriptor imageDescriptor = imageRegistry.getDescriptor(key); + if(imageDescriptor==null) { + imageDescriptor = doCreateDescriptor(key); + imageRegistry.put(key, imageDescriptor); + } + return imageDescriptor; + } + } catch(Exception ex) { + MavenLogger.log(key, ex); + } + return null; + } + + private static Image createImage(String key) { + createDescriptor(key); + ImageRegistry imageRegistry = getImageRegistry(); + return imageRegistry == null ? null : imageRegistry.get(key); + } + + private static ImageRegistry getImageRegistry() { + MavenPlugin plugin = MavenPlugin.getDefault(); + return plugin == null ? null : plugin.getImageRegistry(); + } + + private static ImageDescriptor doCreateDescriptor(String image) { + return AbstractUIPlugin.imageDescriptorFromPlugin(IMavenConstants.PLUGIN_ID, "icons/" + image); //$NON-NLS-1$ + } + + + + private static ImageDescriptor createImageDescriptor( String key, ImageData imageData ) + { + try + { + ImageRegistry imageRegistry = getImageRegistry(); + if ( imageRegistry != null ) + { + ImageDescriptor imageDescriptor = imageRegistry.getDescriptor( key ); + if ( imageDescriptor != null ) + { + imageRegistry.remove( key ); + } + { + imageDescriptor = ImageDescriptor.createFromImageData( imageData ); + imageRegistry.put( key, imageDescriptor ); + } + return imageDescriptor; + } + } + catch ( Exception ex ) + { + MavenLogger.log(key, ex); + } + return null; + } + + private static ImageDescriptor getOverlayImageDescriptor( String basekey, String overlaykey, int quadrant ) + { + String key = basekey + overlaykey; + try + { + ImageRegistry imageRegistry = getImageRegistry(); + if ( imageRegistry != null ) + { + ImageDescriptor imageDescriptor = imageRegistry.getDescriptor( key ); + if ( imageDescriptor == null ) + { + ImageDescriptor base = createDescriptor( basekey ); + ImageDescriptor overlay = createDescriptor( overlaykey ); + if ( base == null || overlay == null ) + { + MavenLogger.log( "cannot construct overlay image descriptor for " + basekey + " " + overlaykey ); + return null; + } + imageDescriptor = createOverlayDescriptor( base, overlay, quadrant ); + imageRegistry.put( key, imageDescriptor ); + } + return imageDescriptor; + } + } + catch ( Exception ex ) + { + MavenLogger.log(key, ex); + } + return null; + } + + public static Image getOverlayImage( String base, String overlay, int quadrant ) + { + getOverlayImageDescriptor( base, overlay, quadrant ); + ImageRegistry imageRegistry = getImageRegistry(); + return imageRegistry == null ? null : imageRegistry.get( base + overlay ); + } + + + private static ImageDescriptor createOverlayDescriptor( ImageDescriptor base, ImageDescriptor overlay, int quadrant ) + { + return new DecorationOverlayIcon( base.createImage(), overlay, quadrant ); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java new file mode 100644 index 00000000..07717168 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java @@ -0,0 +1,564 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.MessageDialogWithToggle; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IStartup; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWebBrowser; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import org.codehaus.plexus.ContainerConfiguration; +import org.codehaus.plexus.DefaultContainerConfiguration; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.MutablePlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +import org.apache.maven.archetype.Archetype; +import org.apache.maven.archetype.common.ArchetypeArtifactManager; +import org.apache.maven.archetype.source.ArchetypeDataSource; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.manager.WagonManager; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.resolver.ArtifactCollector; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.index.ArtifactContextProducer; +import org.apache.maven.index.NexusIndexer; +import org.apache.maven.index.updater.IndexUpdater; +import org.apache.maven.plugin.LegacySupport; + +import org.sonatype.aether.RepositorySystem; + +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeManager; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.embedder.MavenRuntimeManager; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.internal.ExtensionReader; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.console.MavenConsoleImpl; +import org.eclipse.m2e.core.internal.embedder.MavenConfigurationImpl; +import org.eclipse.m2e.core.internal.embedder.MavenEmbeddedRuntime; +import org.eclipse.m2e.core.internal.embedder.MavenImpl; +import org.eclipse.m2e.core.internal.embedder.MavenWorkspaceRuntime; +import org.eclipse.m2e.core.internal.index.IndexesExtensionReader; +import org.eclipse.m2e.core.internal.index.IndexingTransferListener; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; +import org.eclipse.m2e.core.internal.project.MavenMarkerManager; +import org.eclipse.m2e.core.internal.project.ProjectConfigurationManager; +import org.eclipse.m2e.core.internal.project.WorkspaceStateWriter; +import org.eclipse.m2e.core.internal.project.registry.ProjectRegistryManager; +import org.eclipse.m2e.core.internal.project.registry.ProjectRegistryRefreshJob; +import org.eclipse.m2e.core.internal.repository.RepositoryRegistry; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; +import org.eclipse.m2e.core.util.search.IndexSearchEngine; +import org.eclipse.m2e.core.util.search.SearchEngine; + + +/** + * MavenPlugin main plug-in class. + */ +public class MavenPlugin extends AbstractUIPlugin implements IStartup { + + // preferences + private static final String PREFS_ARCHETYPES = "archetypesInfo.xml"; //$NON-NLS-1$ + + // The shared instance + private static MavenPlugin plugin; + + /** + * General purpose plexus container. Contains components from maven embedder and all other bundles visible from this + * bundle's classloader. + */ + private MutablePlexusContainer plexus; + + private MavenConsole console; + + private MavenModelManager modelManager; + + private NexusIndexManager indexManager; + + private BundleContext bundleContext; + + private MavenProjectManager projectManager; + + private MavenRuntimeManager runtimeManager; + + private ProjectConfigurationManager configurationManager; + + private ProjectRegistryRefreshJob mavenBackgroundJob; + + private ArchetypeManager archetypeManager; + + private ProjectRegistryManager managerImpl; + + private IMavenMarkerManager mavenMarkerManager; + + private RepositoryRegistry repositoryRegistry; + + private String version = "0.0.0"; //$NON-NLS-1$ + + private String qualifiedVersion = "0.0.0.qualifier"; //$NON-NLS-1$ + + private IMavenConfiguration mavenConfiguration; + + private MavenImpl maven; + + public MavenPlugin() { + plugin = this; + + if(Boolean.parseBoolean(Platform.getDebugOption(IMavenConstants.PLUGIN_ID + "/debug/initialization"))) { //$NON-NLS-1$ + System.err.println("### executing constructor " + IMavenConstants.PLUGIN_ID); //$NON-NLS-1$ + new Throwable().printStackTrace(); + } + } + + public IMaven getMaven() { + return maven; + } + + /** + * This method is called upon plug-in activation + */ + public void start(final BundleContext context) throws Exception { + super.start(context); + + if(Boolean.parseBoolean(Platform.getDebugOption(IMavenConstants.PLUGIN_ID + "/debug/initialization"))) { //$NON-NLS-1$ + System.err.println("### executing start() " + IMavenConstants.PLUGIN_ID); //$NON-NLS-1$ + new Throwable().printStackTrace(); + } + + this.bundleContext = context; + + try { + this.qualifiedVersion = (String) getBundle().getHeaders().get(Constants.BUNDLE_VERSION); + Version bundleVersion = Version.parseVersion(this.qualifiedVersion); + this.version = bundleVersion.getMajor() + "." + bundleVersion.getMinor() + "." + bundleVersion.getMicro(); //$NON-NLS-1$ //$NON-NLS-2$ + } catch(IllegalArgumentException e) { + // ignored + } + + MavenLogger.setLog(getLog()); + + try { + this.console = new MavenConsoleImpl(MavenImages.M2); //$NON-NLS-1$ + } catch(RuntimeException ex) { + MavenLogger.log(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, "Unable to start console: " + + ex.toString(), ex)); + } + + this.mavenConfiguration = new MavenConfigurationImpl(getPreferenceStore()); + + ClassLoader cl = MavenPlugin.class.getClassLoader(); + ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld(new ClassWorld("plexus.core", cl)) //$NON-NLS-1$ + .setName("plexus"); //$NON-NLS-1$ + this.plexus = new DefaultPlexusContainer(cc); + + File stateLocationDir = getStateLocation().toFile(); + + // TODO this is broken, need to make it lazy, otherwise we'll deadlock or timeout... or both + this.archetypeManager = newArchetypeManager(stateLocationDir); + try { + this.archetypeManager.readCatalogs(); + } catch(Exception ex) { + String msg = "Can't read archetype catalog configuration"; + this.console.logError(msg + "; " + ex.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + } + + this.mavenMarkerManager = new MavenMarkerManager(console, mavenConfiguration); + + boolean updateProjectsOnStartup = mavenConfiguration.isUpdateProjectsOnStartup(); + + //////////////////////////////////////////////////////////////////////////////////////////////// + + this.maven = new MavenImpl(mavenConfiguration, console); + + // TODO eagerly reads workspace state cache + this.managerImpl = new ProjectRegistryManager(maven, console, stateLocationDir, + !updateProjectsOnStartup /* readState */, mavenMarkerManager); + + this.mavenBackgroundJob = new ProjectRegistryRefreshJob(managerImpl, console, mavenConfiguration); + + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + workspace.addResourceChangeListener(mavenBackgroundJob, IResourceChangeEvent.POST_CHANGE + | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE); + + this.projectManager = new MavenProjectManager(managerImpl, mavenBackgroundJob, stateLocationDir); + this.projectManager.addMavenProjectChangedListener(new WorkspaceStateWriter(projectManager)); + if(updateProjectsOnStartup || managerImpl.getProjects().length == 0) { + this.projectManager.refresh(new MavenUpdateRequest(workspace.getRoot().getProjects(), // + mavenConfiguration.isOffline() /*offline*/, false /* updateSnapshots */)); + } + + this.modelManager = new MavenModelManager(maven, projectManager, console); + + this.runtimeManager = new MavenRuntimeManager(getPreferenceStore()); + this.runtimeManager.setEmbeddedRuntime(new MavenEmbeddedRuntime(getBundleContext())); + this.runtimeManager.setWorkspaceRuntime(new MavenWorkspaceRuntime(projectManager)); + + this.configurationManager = new ProjectConfigurationManager(maven, console, projectManager, modelManager, + mavenMarkerManager, mavenConfiguration); + this.projectManager.addMavenProjectChangedListener(this.configurationManager); + + //create repository registry + this.repositoryRegistry = new RepositoryRegistry(maven, projectManager); + this.maven.addSettingsChangeListener(repositoryRegistry); + this.projectManager.addMavenProjectChangedListener(repositoryRegistry); + + //create the index manager + this.indexManager = new NexusIndexManager(console, projectManager, repositoryRegistry, stateLocationDir); + this.projectManager.addMavenProjectChangedListener(indexManager); + this.maven.addLocalRepositoryListener(new IndexingTransferListener(indexManager)); + this.repositoryRegistry.addRepositoryIndexer(indexManager); + this.repositoryRegistry.addRepositoryDiscoverer(new IndexesExtensionReader(indexManager)); + + // fork repository registry update. must after index manager registered as a listener + this.repositoryRegistry.updateRegistry(); + + checkJdk(); + } + + private static ArchetypeManager newArchetypeManager(File stateLocationDir) { + ArchetypeManager archetypeManager = new ArchetypeManager(new File(stateLocationDir, PREFS_ARCHETYPES)); + archetypeManager.addArchetypeCatalogFactory(new ArchetypeCatalogFactory.NexusIndexerCatalogFactory()); + archetypeManager.addArchetypeCatalogFactory(new ArchetypeCatalogFactory.InternalCatalogFactory()); + archetypeManager.addArchetypeCatalogFactory(new ArchetypeCatalogFactory.DefaultLocalCatalogFactory()); + for(ArchetypeCatalogFactory archetypeCatalogFactory : ExtensionReader.readArchetypeExtensions()) { + archetypeManager.addArchetypeCatalogFactory(archetypeCatalogFactory); + } + return archetypeManager; + } + + public void earlyStartup() { + // nothing to do here, all startup work is done in #start(BundleContext) + } + + public PlexusContainer getPlexusContainer() { + return plexus; + } + + /** + * This method is called when the plug-in is stopped + */ + public void stop(BundleContext context) throws Exception { + super.stop(context); + + this.mavenBackgroundJob.cancel(); + try { + this.mavenBackgroundJob.join(); + } catch(InterruptedException ex) { + // ignored + } + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + workspace.removeResourceChangeListener(this.mavenBackgroundJob); + this.mavenBackgroundJob = null; + + this.projectManager.removeMavenProjectChangedListener(this.configurationManager); + this.projectManager.removeMavenProjectChangedListener(indexManager); + this.projectManager.removeMavenProjectChangedListener(repositoryRegistry); + this.projectManager = null; + + this.plexus.dispose(); + this.maven.disposeContainer(); + + this.configurationManager = null; + + if(this.console != null) { + this.console.shutdown(); + } + + plugin = null; + } + + private void checkJdk() { + if(getPreferenceStore().getBoolean(MavenPreferenceConstants.P_DISABLE_JDK_CHECK)) { + return; + } + // There is no tools.jar on Mac OS X + // http://developer.apple.com/documentation/Java/Conceptual/Java14Development/02-JavaDevTools/JavaDevTools.html + String osName = System.getProperty("os.name", ""); //$NON-NLS-1$ //$NON-NLS-2$ + if(osName.toLowerCase().indexOf("mac os") == -1) { //$NON-NLS-1$ + String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ + File toolsJar = new File(javaHome, "../lib/tools.jar"); //$NON-NLS-1$ + if(!toolsJar.exists()) { + getConsole().logError("Eclipse is running in a JRE, but a JDK is required\n" // + + " Some Maven plugins may not work when importing projects or updating source folders."); + if(!getPreferenceStore().getBoolean(MavenPreferenceConstants.P_DISABLE_JDK_WARNING)) { + showJdkWarning(); + } + } + } + } + + private void showJdkWarning() { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell(); + MessageDialogWithToggle dialog = new MessageDialogWithToggle(shell, // + Messages.MavenPlugin_error_jre_title, // + null, Messages.MavenPlugin_error_jre_message, MessageDialog.WARNING, // + new String[] {IDialogConstants.OK_LABEL}, // + 0, Messages.MavenPlugin_error_warn_again, false) { + protected Control createMessageArea(Composite composite) { + Image image = getImage(); + if(image != null) { + imageLabel = new Label(composite, SWT.NULL); + image.setBackground(imageLabel.getBackground()); + imageLabel.setImage(image); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.BEGINNING).applyTo(imageLabel); + } + + Link link = new Link(composite, getMessageLabelStyle()); + link.setText(message); + link.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + if("eclipse.ini".equals(e.text)) { //$NON-NLS-1$ +// String href = "topic=/org.eclipse.platform.doc.user/tasks/running_eclipse.htm"; +// BaseHelpSystem.getHelpDisplay().displayHelpResource(href, false); + + try { + IWebBrowser browser = PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser(); + // browser.openURL(new URL("http://www.eclipse.org/swt/launcher.html")); + browser + .openURL(new URL( + "http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.platform.doc.user/tasks/running_eclipse.htm")); //$NON-NLS-1$ + } catch(MalformedURLException ex) { + MavenLogger.log("Malformed URL", ex); + } catch(PartInitException ex) { + MavenLogger.log(ex); + } + } else { + PreferencesUtil.createPreferenceDialogOn(getShell(), + "org.eclipse.jdt.debug.ui.preferences.VMPreferencePage", null, null).open(); //$NON-NLS-1$ + } + } + }); + + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false) + .hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), SWT.DEFAULT) + .applyTo(link); + + return composite; + } + }; + + dialog.setPrefStore(getPreferenceStore()); + dialog.setPrefKey(MavenPreferenceConstants.P_DISABLE_JDK_WARNING); + + dialog.open(); + + getPreferenceStore().setValue(MavenPreferenceConstants.P_DISABLE_JDK_WARNING, dialog.getToggleState()); + } + }); + } + + /** + * Returns the shared instance. + */ + public static MavenPlugin getDefault() { + return plugin; + } + + public MavenModelManager getMavenModelManager() { + return this.modelManager; + } + + public MavenProjectManager getMavenProjectManager() { + return this.projectManager; + } + + public ProjectRegistryManager getMavenProjectManagerImpl() { + return this.managerImpl; + } + + public IndexManager getIndexManager() { + return this.indexManager; + } + + public MavenConsole getConsole() { + return this.console; + } + + public MavenRuntimeManager getMavenRuntimeManager() { + return this.runtimeManager; + } + + public ArchetypeManager getArchetypeManager() { + return this.archetypeManager; + } + + public IMavenMarkerManager getMavenMarkerManager() { + return this.mavenMarkerManager; + } + + public IMavenConfiguration getMavenConfiguration() { + return this.mavenConfiguration; + } + + /** + * Returns an Image for the file at the given relative path. + */ + public static Image getImage(String path) { + ImageRegistry registry = getDefault().getImageRegistry(); + Image image = registry.get(path); + if(image == null) { + registry.put(path, imageDescriptorFromPlugin(IMavenConstants.PLUGIN_ID, path)); + image = registry.get(path); + } + return image; + } + + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(IMavenConstants.PLUGIN_ID, path); + } + + public BundleContext getBundleContext() { + return this.bundleContext; + } + + public IProjectConfigurationManager getProjectConfigurationManager() { + return configurationManager; + } + + /** for use by unit tests */ + public ProjectRegistryRefreshJob getProjectManagerRefreshJob() { + return mavenBackgroundJob; + } + + private <C> C lookup(Class<C> role) { + try { + return plexus.lookup(role); + } catch(ComponentLookupException ex) { + throw new NoSuchComponentException(ex); + } + } + + private <T> T lookup(Class<T> role, String roleHint) { + try { + return plexus.lookup(role, roleHint); + } catch(ComponentLookupException ex) { + throw new NoSuchComponentException(ex); + } + } + + public static String getVersion() { + return plugin.version; + } + + public static String getQualifiedVersion() { + return plugin.qualifiedVersion; + } + + public IRepositoryRegistry getRepositoryRegistry() { + return repositoryRegistry; + } + + public Archetype getArchetype() { + return lookup(Archetype.class); + } + + public ArchetypeDataSource getArchetypeDataSource(String hint) { + return lookup(ArchetypeDataSource.class, hint); + } + + public ArchetypeArtifactManager getArchetypeArtifactManager() { + return lookup(ArchetypeArtifactManager.class); + } + + public IndexUpdater getIndexUpdater() { + return lookup(IndexUpdater.class); + } + + public WagonManager getWagonManager() { + return lookup(WagonManager.class); + } + + public NexusIndexer getNexusIndexer() { + return lookup(NexusIndexer.class); + } + + public ArtifactContextProducer getArtifactContextProducer() { + return lookup(ArtifactContextProducer.class); + } + + public ArtifactFactory getArtifactFactory() { + return lookup(ArtifactFactory.class); + } + + public ArtifactMetadataSource getArtifactMetadataSource() { + return lookup(ArtifactMetadataSource.class); + } + + public ArtifactCollector getArtifactCollector() { + return lookup(ArtifactCollector.class); + } + + public RepositorySystem getRepositorySystem() { + return lookup(RepositorySystem.class); + } + + public MavenSession setSession(MavenSession session) { + LegacySupport legacy = lookup(LegacySupport.class); + MavenSession old = legacy.getSession(); + legacy.setSession(session); + return old; + } + + public SearchEngine getSearchEngine(IProject context) throws CoreException { + return new IndexSearchEngine(MavenPlugin.getDefault().getIndexManager().getIndex(context)); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/NoSuchComponentException.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/NoSuchComponentException.java new file mode 100644 index 00000000..b79b7ddf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/NoSuchComponentException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core; + +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; + +/** + * NoSuchComponentException + * + * @author igor + */ +public class NoSuchComponentException extends IllegalArgumentException { + + private static final long serialVersionUID = 9184391358528175461L; + + public NoSuchComponentException(ComponentLookupException ex) { + super(ex); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AbstractMavenMenuCreator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AbstractMavenMenuCreator.java new file mode 100644 index 00000000..92a431a5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AbstractMavenMenuCreator.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IActionDelegate; + +import org.eclipse.m2e.core.MavenPlugin; + + +/** + * Abstract Maven menu creator can be used to contribute custom entries to the Maven popup menu. + * <p> + * Custom items can be added to one of the standard groups {@link #NEW}, {@link #OPEN}, {@link #UPDATE}, {@link #NATURE} + * or {@link #IMPORT}. + * + * @see org.eclipse.m2e.m2menu extension point + * + * @author Eugene Kuleshov + */ +public abstract class AbstractMavenMenuCreator { + public static final String NEW = "new"; //$NON-NLS-1$ + public static final String OPEN = "open"; //$NON-NLS-1$ + public static final String UPDATE = "update"; //$NON-NLS-1$ + public static final String NATURE = "nature"; //$NON-NLS-1$ + public static final String IMPORT = "import"; //$NON-NLS-1$ + + protected IStructuredSelection selection; + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } + } + + /** + * Creates menu items in given menu manager. + */ + public abstract void createMenu(IMenuManager mgr); + + /** + * A helper method to create IAction instance from given IActionDelegate. + */ + protected IAction getAction(IActionDelegate delegate, String id, String text) { + return getAction(delegate, id, text, (ImageDescriptor) null); + } + + /** + * A helper method to create IAction instance from given IActionDelegate. + */ + protected IAction getAction(IActionDelegate delegate, String id, String text, String image) { + return getAction(delegate, id, text, MavenPlugin.getImageDescriptor(image)); + } + + protected IAction getAction(IActionDelegate delegate, String id, String text, ImageDescriptor image) { + ActionProxy action = new ActionProxy(id, text, delegate); + if(image!=null) { + action.setImageDescriptor(image); + } + return action; + } + + class ActionProxy extends Action { + private IActionDelegate action; + + public ActionProxy(String id, String text, IActionDelegate action) { + super(text); + this.action = action; + setId(id); + } + + public ActionProxy(String id, String text, IActionDelegate action, int style) { + super(text, style); + this.action = action; + setId(id); + } + + public void run() { + action.selectionChanged(this, selection); + action.run(this); + } + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddDependencyAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddDependencyAction.java new file mode 100644 index 00000000..316b44d5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddDependencyAction.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; + +import org.apache.maven.model.Dependency; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; + + +public class AddDependencyAction extends MavenActionSupport implements IWorkbenchWindowActionDelegate { + + public static final String ID = "org.eclipse.m2e.addDependencyAction"; //$NON-NLS-1$ + + public void run(IAction action) { + IFile file = getPomFileFromPomEditorOrViewSelection(); + + if(file == null) { + return; + } + + MavenPlugin plugin = MavenPlugin.getDefault(); + + Set<ArtifactKey> artifacts = getArtifacts(file, plugin); + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(getShell(), Messages.AddDependencyAction_searchDialog_title, IIndex.SEARCH_ARTIFACT, artifacts, true); + if(dialog.open() == Window.OK) { + IndexedArtifactFile indexedArtifactFile = (IndexedArtifactFile) dialog.getFirstResult(); + if(indexedArtifactFile != null) { + try { + MavenModelManager modelManager = plugin.getMavenModelManager(); + Dependency dependency = indexedArtifactFile.getDependency(); + String selectedScope = dialog.getSelectedScope(); + dependency.setScope(selectedScope); + modelManager.addDependency(file, dependency); + } catch(Exception ex) { + String msg = NLS.bind(Messages.AddDependencyAction_error_msg, file); + MavenLogger.log(msg, ex); + MessageDialog.openError(Display.getCurrent().getActiveShell(), Messages.AddDependencyAction_error_title, msg); + } + } + } + } + + public void dispose() { + } + + public void init(IWorkbenchWindow window) { + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddPluginAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddPluginAction.java new file mode 100644 index 00000000..5251d527 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/AddPluginAction.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Collections; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.window.Window; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; + + +public class AddPluginAction extends MavenActionSupport implements IWorkbenchWindowActionDelegate { + + public static final String ID = "org.eclipse.m2e.addPluginAction"; //$NON-NLS-1$ + + public void run(IAction action) { + IFile file = getPomFileFromPomEditorOrViewSelection(); + + if(file == null) { + return; + } + + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(getShell(), Messages.AddPluginAction_searchDialog_title, IIndex.SEARCH_PLUGIN, Collections.<ArtifactKey> emptySet()); + if(dialog.open() == Window.OK) { + final IndexedArtifactFile indexedArtifactFile = (IndexedArtifactFile) dialog.getFirstResult(); + if(indexedArtifactFile != null) { + try { + MavenModelManager modelManager = MavenPlugin.getDefault().getMavenModelManager(); + modelManager.updateProject(file, new MavenModelManager.PluginAdder( // + indexedArtifactFile.group, // + indexedArtifactFile.artifact, // + indexedArtifactFile.version)); + } catch(Exception ex) { + MavenLogger.log("Can't add dependency to " + file, ex); + } + } + } + } + + public void dispose() { + } + + public void init(IWorkbenchWindow window) { + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ChangeNatureAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ChangeNatureAction.java new file mode 100644 index 00000000..ef074ace --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ChangeNatureAction.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.project.ResolverConfiguration; + + +public class ChangeNatureAction implements IObjectActionDelegate { + + public static final String ID_ENABLE_WORKSPACE = "org.eclipse.m2e.enableWorkspaceResolutionAction"; //$NON-NLS-1$ + + public static final String ID_DISABLE_WORKSPACE = "org.eclipse.m2e.disableWorkspaceResolutionAction"; //$NON-NLS-1$ + + public static final int ENABLE_WORKSPACE = 1; + + public static final int DISABLE_WORKSPACE = 2; + + private ISelection selection; + + private int option; + + public ChangeNatureAction(int option) { + this.option = option; + } + + public void selectionChanged(IAction action, ISelection selection) { + this.selection = selection; + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + public void run(IAction action) { + if(selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + Set<IProject> projects = new LinkedHashSet<IProject>(); + for(Iterator<?> it = structuredSelection.iterator(); it.hasNext();) { + Object element = it.next(); + IProject project = null; + if(element instanceof IProject) { + project = (IProject) element; + } else if(element instanceof IAdaptable) { + project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); + } + if(project != null) { + projects.add(project); + } + } + + new UpdateJob(projects, option).schedule(); + } + } + + static class UpdateJob extends WorkspaceJob { + private final Set<IProject> projects; + private final int option; + + private final IProjectConfigurationManager importManager; + private final MavenProjectManager projectManager; + private final IMavenConfiguration mavenConfiguration; + + public UpdateJob(Set<IProject> projects, int option) { + super(Messages.ChangeNatureAction_job_changing); + this.projects = projects; + this.option = option; + + MavenPlugin plugin = MavenPlugin.getDefault(); + this.importManager = plugin.getProjectConfigurationManager(); + this.projectManager = plugin.getMavenProjectManager(); + + this.mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + } + + public IStatus runInWorkspace(IProgressMonitor monitor) { + MultiStatus status = null; + for(IProject project : projects) { + if (monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + monitor.subTask(project.getName()); + + try { + changeNature(project, monitor); + } catch (CoreException ex) { + if (status == null) { + status = new MultiStatus(IMavenConstants.PLUGIN_ID, IStatus.ERROR, Messages.ChangeNatureAction_status_error, null); + } + status.add(ex.getStatus()); + } + } + + boolean offline = mavenConfiguration.isOffline(); + boolean updateSnapshots = false; + projectManager.refresh(new MavenUpdateRequest(projects.toArray(new IProject[projects.size()]), // + offline, updateSnapshots)); + + return status != null? status: Status.OK_STATUS; + } + + private void changeNature(final IProject project, IProgressMonitor monitor) throws CoreException { + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenProjectManager projectManager = plugin.getMavenProjectManager(); + + final ResolverConfiguration configuration = projectManager.getResolverConfiguration(project); + + boolean updateSourceFolders = false; + + switch(option) { + case ENABLE_WORKSPACE: + configuration.setResolveWorkspaceProjects(true); + break; + case DISABLE_WORKSPACE: + configuration.setResolveWorkspaceProjects(false); + break; + } + + projectManager.setResolverConfiguration(project, configuration); + + if (updateSourceFolders) { + importManager.updateProjectConfiguration(project, configuration, monitor); + } + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/CheckoutAsMavenAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/CheckoutAsMavenAction.java new file mode 100644 index 00000000..9431826e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/CheckoutAsMavenAction.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Iterator; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.scm.ScmUrl; +import org.eclipse.m2e.core.wizards.MavenCheckoutWizard; + + +/** + * Checkout as Maven project action + * + * @author @author Eugene Kuleshov + */ +public class CheckoutAsMavenAction implements IObjectActionDelegate { + + private IStructuredSelection selection; + private IWorkbenchPart targetPart; + + /* (non-Javadoc) + * @see org.eclipse.ui.actions.ActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + if (selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + this.targetPart = targetPart; + } + + + /* (non-Javadoc) + * @see org.eclipse.ui.actions.ActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + ScmUrl[] urls = null; + if(selection != null) { + urls = new ScmUrl[selection.size()]; + int i = 0; + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + urls[i++] = (ScmUrl) it.next(); + } + } + + MavenCheckoutWizard wizard = new MavenCheckoutWizard(urls); + WizardDialog dialog = new WizardDialog(getShell(), wizard); + dialog.open(); + } + + protected Shell getShell() { + Shell shell = null; + if(targetPart != null) { + shell = targetPart.getSite().getShell(); + } + if(shell != null) { + return shell; + } + + IWorkbench workbench = MavenPlugin.getDefault().getWorkbench(); + if(workbench == null) { + return null; + } + + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + return window == null ? null : window.getShell(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/DisableNatureAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/DisableNatureAction.java new file mode 100644 index 00000000..7ae99170 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/DisableNatureAction.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Iterator; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; + + +public class DisableNatureAction implements IObjectActionDelegate { + public static final String ID = "org.eclipse.m2e.disableAction"; //$NON-NLS-1$ + + private ISelection selection; + + /* + * (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + if(selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + for(Iterator<?> it = structuredSelection.iterator(); it.hasNext();) { + Object element = it.next(); + IProject project = null; + if(element instanceof IProject) { + project = (IProject) element; + } else if(element instanceof IAdaptable) { + project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); + } + if(project != null) { + MavenPlugin plugin = MavenPlugin.getDefault(); + + try { + plugin.getProjectConfigurationManager().disableMavenNature(project, new NullProgressMonitor()); + + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, + * org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + this.selection = selection; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, + * org.eclipse.ui.IWorkbenchPart) + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/EnableNatureAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/EnableNatureAction.java new file mode 100644 index 00000000..2dec17c2 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/EnableNatureAction.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Iterator; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.ResolverConfiguration; +import org.eclipse.m2e.core.wizards.MavenPomWizard; + + +public class EnableNatureAction implements IObjectActionDelegate, IExecutableExtension { + + public static final String ID = "org.eclipse.m2e.enableNatureAction"; //$NON-NLS-1$ + + static final String ID_WORKSPACE = "org.eclipse.m2e.enableWorkspaceResolutionAction"; //$NON-NLS-1$ + + static final String ID_MODULES = "org.eclipse.m2e.enableModulesAction"; //$NON-NLS-1$ + + private boolean workspaceProjects = true; + + private ISelection selection; + + public EnableNatureAction() { + } + + public EnableNatureAction(String option) { + setInitializationData(null, null, option); + } + + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + if(IMavenConstants.NO_WORKSPACE_PROJECTS.equals(data)) { + this.workspaceProjects = false; + } + } + + public void selectionChanged(IAction action, ISelection selection) { + this.selection = selection; + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + public void run(IAction action) { + if(selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + for(Iterator<?> it = structuredSelection.iterator(); it.hasNext();) { + Object element = it.next(); + IProject project = null; + if(element instanceof IProject) { + project = (IProject) element; + } else if(element instanceof IAdaptable) { + project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); + } + if(project != null) { + enableNature(project, structuredSelection.size() == 1); + } + } + } + } + + private void enableNature(final IProject project, boolean isSingle) { + final MavenPlugin plugin = MavenPlugin.getDefault(); + IFile pom = project.getFile(IMavenConstants.POM_FILE_NAME); + if(isSingle && !pom.exists()) { + // XXX move into AbstractProjectConfigurator and use Eclipse project settings + IWorkbench workbench = plugin.getWorkbench(); + + MavenPomWizard wizard = new MavenPomWizard(); + wizard.init(workbench, (IStructuredSelection) selection); + + Shell shell = workbench.getActiveWorkbenchWindow().getShell(); + WizardDialog wizardDialog = new WizardDialog(shell, wizard); + wizardDialog.create(); + wizardDialog.getShell().setText(Messages.EnableNatureAction_wizard_shell); + if(wizardDialog.open() == Window.CANCEL) { + return; + } + } + Job job = new Job(Messages.EnableNatureAction_job_enable) { + + protected IStatus run(IProgressMonitor monitor) { + try { + ResolverConfiguration configuration = new ResolverConfiguration(); + configuration.setResolveWorkspaceProjects(workspaceProjects); + configuration.setActiveProfiles(""); //$NON-NLS-1$ + + boolean hasMavenNature = project.hasNature(IMavenConstants.NATURE_ID); + + IProjectConfigurationManager configurationManager = plugin.getProjectConfigurationManager(); + + configurationManager.enableMavenNature(project, configuration, new NullProgressMonitor()); + + if(!hasMavenNature) { + configurationManager.updateProjectConfiguration(project, configuration, monitor); + } + } catch(CoreException ex) { + MavenLogger.log(ex); + } + return Status.OK_STATUS; + } + }; + job.schedule(); + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MaterializeAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MaterializeAction.java new file mode 100644 index 00000000..fee02f4f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MaterializeAction.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; + +import org.apache.maven.model.Dependency; + +import org.eclipse.m2e.core.wizards.MavenMaterializePomWizard; + + +public class MaterializeAction implements IObjectActionDelegate { + + public static final String ID = "org.eclipse.m2e.materializeAction"; //$NON-NLS-1$ + + private IStructuredSelection selection; + + public void run(IAction action) { + MavenMaterializePomWizard wizard = new MavenMaterializePomWizard(); + wizard.init(PlatformUI.getWorkbench(), selection); + + Dependency[] dependencies = wizard.getDependencies(); + if(dependencies!=null && dependencies.length>0) { + WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), wizard); + dialog.open(); + } else { + // TODO show info dialog + } + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } else { + this.selection = null; + } + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenActionSupport.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenActionSupport.java new file mode 100644 index 00000000..9e9ff69c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenActionSupport.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ArtifactRef; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; + +/** + * + * MavenActionSupport + * + * @author Jason van Zyl + */ +public abstract class MavenActionSupport implements IObjectActionDelegate { + protected IStructuredSelection selection; + + protected IWorkbenchPart targetPart; + + protected Set<ArtifactKey> getArtifacts(IFile file, MavenPlugin plugin) { + try { + MavenProjectManager projectManager = plugin.getMavenProjectManager(); + IMavenProjectFacade projectFacade = projectManager.create(file, true, new NullProgressMonitor()); + if(projectFacade != null) { + return ArtifactRef.toArtifactKey(projectFacade.getMavenProjectArtifacts()); + } + } catch(Exception ex) { + String msg = "Can't read Maven project"; + MavenLogger.log(msg, ex); + plugin.getConsole().logError(msg + "; " + ex.toString()); + } + return Collections.emptySet(); + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + this.targetPart = targetPart; + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } + } + + protected Shell getShell() { + Shell shell = null; + if(targetPart != null) { + shell = targetPart.getSite().getShell(); + } + if(shell != null) { + return shell; + } + + IWorkbench workbench = MavenPlugin.getDefault().getWorkbench(); + if(workbench == null) { + return null; + } + + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + return window == null ? null : window.getShell(); + } + + protected IFile getPomFileFromPomEditorOrViewSelection() { + IFile file = null; + // + // If I am in the POM editor I want to get hold of the IFile that is currently in the buffer + // + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + + if(window != null) { + IWorkbenchPage page = window.getActivePage(); + if(page != null) { + IEditorPart editor = page.getActiveEditor(); + if(editor != null) { + IEditorInput input = editor.getEditorInput(); + if(input instanceof IFileEditorInput) { + IFileEditorInput fileInput = (IFileEditorInput) input; + file = fileInput.getFile(); + if(file.getName().equals(IMavenConstants.POM_FILE_NAME)) { + return file; + } + } + } + } + } + + // + // Otherwise we will assume a pom.xml file or IProject is being selected in the + // package explorer and we'll get the IFile from that. Otherwise we'll bail. + // + Object o = selection.iterator().next(); + + if(o instanceof IProject) { + file = ((IProject) o).getFile(IMavenConstants.POM_FILE_NAME); + } else if(o instanceof IFile) { + file = (IFile) o; + } else { + file = null; + } + + return file; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenConsoleRemoveAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenConsoleRemoveAction.java new file mode 100644 index 00000000..92aa714d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenConsoleRemoveAction.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.Action; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; + +public class MavenConsoleRemoveAction extends Action { + + public MavenConsoleRemoveAction() { + setToolTipText(Messages.MavenConsoleRemoveAction_tooltip); + setImageDescriptor(MavenImages.CLOSE); + } + + public void run() { + MavenPlugin.getDefault().getConsole().closeConsole(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenDebugOutputAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenDebugOutputAction.java new file mode 100644 index 00000000..c36e5357 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenDebugOutputAction.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +/** + * @author Eugene Kuleshov + */ +public class MavenDebugOutputAction extends Action { + + private IPropertyChangeListener listener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + if(MavenPreferenceConstants.P_DEBUG_OUTPUT.equals(event.getProperty())) { + setChecked(isDebug()); + } + } + }; + + public MavenDebugOutputAction() { + setToolTipText(Messages.MavenDebugOutputAction_0); + setImageDescriptor(MavenImages.DEBUG); + + getPreferenceStore().addPropertyChangeListener(listener); + setChecked(isDebug()); + } + + public void run() { + getPreferenceStore().setValue(MavenPreferenceConstants.P_DEBUG_OUTPUT, isChecked()); + } + + public void dispose() { + getPreferenceStore().removePropertyChangeListener(listener); + } + + IPreferenceStore getPreferenceStore() { + return MavenPlugin.getDefault().getPreferenceStore(); + } + + boolean isDebug() { + return getPreferenceStore().getBoolean(MavenPreferenceConstants.P_DEBUG_OUTPUT); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenMenuAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenMenuAction.java new file mode 100644 index 00000000..6c67de47 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenMenuAction.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.action.GroupMarker; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuCreator; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.events.MenuAdapter; +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.actions.DefaultMavenMenuCreator; + + +/** + * Maven menu action + * + * @author Eugene Kuleshov + */ +public class MavenMenuAction implements IObjectActionDelegate, IMenuCreator { + + private static final String EXTENSION_MENU_ITEMS = IMavenConstants.PLUGIN_ID + ".m2menu"; //$NON-NLS-1$ + + boolean fillMenu; + + IAction delegateAction; + + List<AbstractMavenMenuCreator> creators = null; + + // IObjectActionDelegate + + public void run(IAction action) { + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + // this.selection = (IStructuredSelection) selection; + this.fillMenu = true; + + if(delegateAction != action) { + delegateAction = action; + delegateAction.setMenuCreator(this); + } + + action.setEnabled(!selection.isEmpty()); + + for(AbstractMavenMenuCreator creator : getCreators()) { + creator.selectionChanged(action, selection); + } + } + } + + // IMenuCreator + + public void dispose() { + } + + public Menu getMenu(Control parent) { + return null; + } + + public Menu getMenu(Menu parent) { + Menu menu = new Menu(parent); + + /** + * Add listener to re-populate the menu each time it is shown because MenuManager.update(boolean, boolean) doesn't + * dispose pull-down ActionContribution items for each popup menu. + */ + menu.addMenuListener(new MenuAdapter() { + public void menuShown(MenuEvent e) { + if(fillMenu) { + Menu m = (Menu) e.widget; + + for(MenuItem item : m.getItems()) { + item.dispose(); + } + + IMenuManager mgr = new MenuManager("#maven"); //$NON-NLS-1$ + mgr.add(new GroupMarker(AbstractMavenMenuCreator.NEW)); + mgr.insertAfter(AbstractMavenMenuCreator.NEW, new GroupMarker(AbstractMavenMenuCreator.UPDATE)); + mgr.insertAfter(AbstractMavenMenuCreator.UPDATE, new GroupMarker(AbstractMavenMenuCreator.OPEN)); + mgr.insertAfter(AbstractMavenMenuCreator.OPEN, new GroupMarker(AbstractMavenMenuCreator.NATURE)); + mgr.insertAfter(AbstractMavenMenuCreator.NATURE, new GroupMarker(AbstractMavenMenuCreator.IMPORT)); + + for(AbstractMavenMenuCreator creator : getCreators()) { + creator.createMenu(mgr); + } + + for(IContributionItem item : mgr.getItems()) { + item.fill(m, -1); + } + + fillMenu = false; + } + } + }); + + return menu; + } + + List<AbstractMavenMenuCreator> getCreators() { + if(creators == null) { + creators = new ArrayList<AbstractMavenMenuCreator>(); + creators.add(new DefaultMavenMenuCreator()); + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint extensionPoint = registry.getExtensionPoint(EXTENSION_MENU_ITEMS); + if(extensionPoint!=null) { + for(IExtension extension : extensionPoint.getExtensions()) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + try { + AbstractMavenMenuCreator creator = (AbstractMavenMenuCreator) element.createExecutableExtension("class"); //$NON-NLS-1$ + creators.add(creator); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + return creators; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenPropertyTester.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenPropertyTester.java new file mode 100644 index 00000000..9bf3586f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/MavenPropertyTester.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IAdaptable; + +import org.eclipse.m2e.core.core.IMavenConstants; + +/** + * Helper IPropertyTester implementation to check if receiver can be launched with Maven. + * E.g. it is pom.xml file of folder or project that has pom.xml. + * + * @author Eugene Kuleshov + */ +public class MavenPropertyTester extends PropertyTester { + + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + IAdaptable adaptable = (IAdaptable) receiver; + + IProject projectAdapter = (IProject) adaptable.getAdapter(IProject.class); + if(projectAdapter!=null) { + return projectAdapter.getFile(IMavenConstants.POM_FILE_NAME).exists(); + } + + IFolder folderAdapter = (IFolder) adaptable.getAdapter(IFolder.class); + if(folderAdapter!=null) { + return folderAdapter.getFile(IMavenConstants.POM_FILE_NAME).exists(); + } + + IFile fileAdapter = (IFile) adaptable.getAdapter(IFile.class); + if(fileAdapter!=null) { + return fileAdapter.exists() && IMavenConstants.POM_FILE_NAME.equals(fileAdapter.getName()); + } + + return false; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ModuleProjectWizardAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ModuleProjectWizardAction.java new file mode 100644 index 00000000..ed7223c9 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/ModuleProjectWizardAction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.m2e.core.wizards.MavenModuleWizard; + +/** + * A module project wizard action. + */ +public class ModuleProjectWizardAction implements IObjectActionDelegate { + + /** action id */ + public static final String ID = + "org.eclipse.m2e.actions.moduleProjectWizardAction"; //$NON-NLS-1$ + + /** the current selection */ + private IStructuredSelection selection; + + /** parent shell */ + private Shell parent; + + /** Runs the action. */ + public void run( IAction action ) { + MavenModuleWizard wizard = new MavenModuleWizard(); + wizard.init( PlatformUI.getWorkbench(), selection ); + WizardDialog dialog = new WizardDialog( parent, wizard ); + dialog.open(); + } + + + /** Sets the active workbench part. */ + public void setActivePart( IAction action, IWorkbenchPart part ) { + parent = part.getSite().getShell(); + } + + + /** Handles the selection change */ + public void selectionChanged( IAction action, ISelection selection ) { + if( selection instanceof IStructuredSelection ) { + this.selection = ( IStructuredSelection ) selection; + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenMavenConsoleAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenMavenConsoleAction.java new file mode 100644 index 00000000..47b10f71 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenMavenConsoleAction.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import org.eclipse.jface.action.Action; + +import org.eclipse.m2e.core.MavenPlugin; + +/** + * Open Maven Console Action + * + * @author Eugene Kuleshov + */ +public class OpenMavenConsoleAction extends Action { + + public void run() { + MavenPlugin.getDefault().getConsole().showConsole(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenPomAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenPomAction.java new file mode 100644 index 00000000..700b6c54 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenPomAction.java @@ -0,0 +1,404 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorRegistry; +import org.eclipse.ui.IPathEditorInput; +import org.eclipse.ui.IPersistableElement; +import org.eclipse.ui.IStorageEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.part.FileEditorInput; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; + + +/** + * Open POM Action + * + * @author Eugene Kuleshov + */ +public class OpenPomAction extends ActionDelegate implements IWorkbenchWindowActionDelegate, IExecutableExtension { + + public static final String ID = "org.eclipse.m2e.openPomAction"; //$NON-NLS-1$ + + String type = IIndex.SEARCH_ARTIFACT; + + private IStructuredSelection selection; + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow) + */ + public void init(IWorkbenchWindow window) { + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) + */ + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + if("plugins".equals(data)) { //$NON-NLS-1$ + this.type = IIndex.SEARCH_PACKAGING; + } else { + this.type = IIndex.SEARCH_ARTIFACT; + } + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } else { + this.selection = null; + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.actions.ActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + if(selection != null) { + Object element = this.selection.getFirstElement(); + if(IIndex.SEARCH_ARTIFACT.equals(type) && element != null) { + try { + final ArtifactKey ak = SelectionUtil.getArtifactKey(element); + if(ak != null) { + new Job(Messages.OpenPomAction_job_opening) { + protected IStatus run(IProgressMonitor monitor) { + openEditor(ak.getGroupId(), ak.getArtifactId(), ak.getVersion(), monitor); + return Status.OK_STATUS; + } + }.schedule(); + return; + } + } catch(CoreException ex) { + MavenLogger.log(ex); + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + MessageDialog.openInformation(Display.getDefault().getActiveShell(), // + Messages.OpenPomAction_open_error_title, Messages.OpenPomAction_open_error_message); + } + }); + } + } + } + + String title = Messages.OpenPomAction_title_pom; + + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(shell, title, type, Collections + .<ArtifactKey> emptySet()); + if(dialog.open() == Window.OK) { + final IndexedArtifactFile iaf = (IndexedArtifactFile) dialog.getFirstResult(); + new Job(Messages.OpenPomAction_job_opening) { + protected IStatus run(IProgressMonitor monitor) { + if(iaf != null) { + openEditor(iaf.group, iaf.artifact, iaf.version, monitor); + } + return Status.OK_STATUS; + } + }.schedule(); + } + } + + public static void openEditor(IndexedArtifact ia, IndexedArtifactFile f, IProgressMonitor monitor) { + if(f == null || ia.getClassname() == null || ia.getPackageName() == null) { + return; + } + + String groupId = f.getDependency().getGroupId(); + String artifactId = f.getDependency().getArtifactId(); + String version = f.getDependency().getVersion(); + + String name = ia.getClassname(); + String fileName = ia.getPackageName().replace('.', '/') + "/" + ia.getClassname() + ".java"; //$NON-NLS-1$ //$NON-NLS-2$ + String tooltip = groupId + ":" + artifactId + ":" + version + "/" + fileName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + try { + IMaven maven = MavenPlugin.getDefault().getMaven(); + + List<ArtifactRepository> artifactRepositories = maven.getArtifactRepositories(); + + Artifact artifact = maven.resolve(groupId, artifactId, version, "java-source", "sources", artifactRepositories, //$NON-NLS-1$ //$NON-NLS-2$ + monitor); + + final File file = artifact.getFile(); + if(file == null) { + openDialog(NLS.bind(Messages.OpenPomAction_error_download_source, tooltip)); + return; + } + + // that won't work if source archive have subfolders before actual source tree + String url = "jar:" + file.toURL().toString() + "!/" + fileName; //$NON-NLS-1$ //$NON-NLS-2$ + InputStream is = new URL(url).openStream(); + byte[] buff = readStream(is); + + openEditor(new MavenPathStorageEditorInput(name + ".java", tooltip, url, buff), name + ".java"); //$NON-NLS-1$ //$NON-NLS-2$ + + } catch(IOException ex) { + String msg = NLS.bind(Messages.OpenPomAction_error_open_editor, name); + MavenLogger.log(msg, ex); + openDialog(msg + "\n" + ex.toString()); //$NON-NLS-1$ + } catch(CoreException ex) { + MavenLogger.log(ex); + openDialog(ex.getMessage() + "\n" + ex.toString()); //$NON-NLS-1$ + } + } + + public static IEditorPart openEditor(String groupId, String artifactId, String version, IProgressMonitor monitor) { + if(groupId.length() > 0 && artifactId.length() > 0) { + final String name = groupId + ":" + artifactId + ":" + version + ".pom"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + try { + MavenPlugin plugin = MavenPlugin.getDefault(); + + MavenProjectManager projectManager = plugin.getMavenProjectManager(); + IMavenProjectFacade projectFacade = projectManager.getMavenProject(groupId, artifactId, version); + if(projectFacade != null) { + final IFile pomFile = projectFacade.getPom(); + return openEditor(new FileEditorInput(pomFile), name); + } + + IMaven maven = MavenPlugin.getDefault().getMaven(); + + List<ArtifactRepository> artifactRepositories = maven.getArtifactRepositories(); + + Artifact artifact = maven.resolve(groupId, artifactId, version, "pom", null, artifactRepositories, monitor); //$NON-NLS-1$ + + File file = artifact.getFile(); + if(file != null) { + return openEditor(new MavenPathStorageEditorInput(name, name, file.getAbsolutePath(), + readStream(new FileInputStream(file))), name); + } + + openDialog(NLS.bind(Messages.OpenPomAction_error_download, name)); + + } catch(IOException ex) { + String msg = NLS.bind(Messages.OpenPomAction_error_open_pom, name); + MavenLogger.log(msg, ex); + openDialog(msg + "\n" + ex.toString()); //$NON-NLS-1$ + } catch(CoreException ex) { + MavenLogger.log(ex); + openDialog(ex.getMessage() + "\n" + ex.toString()); //$NON-NLS-1$ + } + } + + return null; + } + + public static IEditorPart openEditor(final IEditorInput editorInput, final String name) { + final IEditorPart[] part = new IEditorPart[1]; + PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { + public void run() { + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + IContentType contentType = contentTypeManager.findContentTypeFor(name); + IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry(); + IEditorDescriptor editor = editorRegistry.getDefaultEditor(name, contentType); + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if(window != null) { + IWorkbenchPage page = window.getActivePage(); + if(page != null) { + try { + part[0] = page.openEditor(editorInput, editor.getId()); + } catch(PartInitException ex) { + MessageDialog.openInformation(Display.getDefault().getActiveShell(), // + Messages.OpenPomAction_open_title, NLS.bind(Messages.OpenPomAction_33, editorInput.getName(), ex.toString())); //$NON-NLS-1$ + } + } + } + } + }); + return part[0]; + } + + private static void openDialog(final String msg) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + MessageDialog.openInformation(Display.getDefault().getActiveShell(), // + Messages.OpenPomAction_open_title, msg); + } + }); + } + + private static byte[] readStream(InputStream is) throws IOException { + byte[] b = new byte[is.available()]; + int len = 0; + while(true) { + int n = is.read(b, len, b.length - len); + if(n == -1) { + if(len < b.length) { + byte[] c = new byte[len]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + return b; + } + len += n; + if(len == b.length) { + byte[] c = new byte[b.length + 1000]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + } + } + + /** + * Storage editor input implementation for Maven poms + */ + public static class MavenStorageEditorInput implements IStorageEditorInput { + + private final String name; + + private final String path; + + private final String tooltip; + + private final byte[] content; + + public MavenStorageEditorInput(String name, String tooltip, String path, byte[] content) { + this.name = name; + this.path = path; + this.tooltip = tooltip; + this.content = content; + } + + // IStorageEditorInput + + public boolean exists() { + return true; + } + + public String getName() { + return this.name; + } + + public String getToolTipText() { + return this.tooltip; + } + + public IStorage getStorage() { + return new MavenStorage(name, path, content); + } + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public IPersistableElement getPersistable() { + return null; + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + return null; + } + + // IPathEditorInput + + public IPath getPath() { + return new Path(path); + } + + } + + public static class MavenPathStorageEditorInput extends MavenStorageEditorInput implements IPathEditorInput { + public MavenPathStorageEditorInput(String name, String tooltip, String path, byte[] content) { + super(name, tooltip, path, content); + } + } + + private static class MavenStorage implements IStorage { + private String name; + + private final String path; + + private final byte[] content; + + public MavenStorage(String name, String path, byte[] content) { + this.name = name; + this.path = path; + this.content = content; + } + + public String getName() { + return name; + } + + public IPath getFullPath() { + return path == null ? null : new Path(path); + } + + public InputStream getContents() { + return new ByteArrayInputStream(content); + } + + public boolean isReadOnly() { + return true; + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + return null; + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenUrlAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenUrlAction.java new file mode 100644 index 00000000..2b81ec4b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/OpenUrlAction.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +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.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.browser.IWebBrowser; +import org.eclipse.ui.browser.IWorkbenchBrowserSupport; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.model.CiManagement; +import org.apache.maven.model.IssueManagement; +import org.apache.maven.model.Scm; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * Open Url Action + * + * @author Eugene Kuleshov + */ +public class OpenUrlAction extends ActionDelegate implements IWorkbenchWindowActionDelegate, IExecutableExtension { + + public static final String ID_PROJECT = "org.eclipse.m2e.openProjectPage"; //$NON-NLS-1$ + + public static final String ID_ISSUES = "org.eclipse.m2e.openIssuesPage"; //$NON-NLS-1$ + + public static final String ID_SCM = "org.eclipse.m2e.openScmPage"; //$NON-NLS-1$ + + public static final String ID_CI = "org.eclipse.m2e.openCiPage"; //$NON-NLS-1$ + + String actionId; + + private IStructuredSelection selection; + + public OpenUrlAction() { + } + + public OpenUrlAction(String id) { + this.actionId = id; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } else { + this.selection = null; + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + if(selection != null) { + try { + Object element = this.selection.getFirstElement(); + final ArtifactKey a = SelectionUtil.getArtifactKey(element); + if(a != null) { + new Job(Messages.OpenUrlAction_job_browser) { + protected IStatus run(IProgressMonitor monitor) { + openBrowser(actionId, a.getGroupId(), a.getArtifactId(), a.getVersion(), monitor); + return Status.OK_STATUS; + } + + }.schedule(); + return; + } + } catch(CoreException ex) { + MavenLogger.log(ex); + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + MessageDialog.openInformation(Display.getDefault().getActiveShell(), // + Messages.OpenUrlAction_open_url_title, Messages.OpenUrlAction_open_url_message); + } + }); + } + } + } + + public static void openBrowser(String actionId, String groupId, String artifactId, String version, IProgressMonitor monitor) { + try { + MavenProject mavenProject = getMavenProject(groupId, artifactId, version, monitor); + final String url = getUrl(actionId, mavenProject); + if(url!=null) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + try { + IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport(); + IWebBrowser browser = browserSupport.createBrowser(IWorkbenchBrowserSupport.NAVIGATION_BAR + | IWorkbenchBrowserSupport.LOCATION_BAR, url, url, url); + browser.openURL(new URL(url)); + } catch(PartInitException ex) { + MavenLogger.log(ex); + } catch(MalformedURLException ex) { + MavenLogger.log("Malformed url " + url, ex); + } + } + }); + } + } catch(Exception ex) { + MavenLogger.log("Can't open URL", ex); + } + } + + private static String getUrl(String actionId, MavenProject mavenProject) { + String url = null; + if(ID_PROJECT.equals(actionId)) { + url = mavenProject.getUrl(); + if(url == null) { + openDialog(Messages.OpenUrlAction_error_no_url); + } + } else if(ID_ISSUES.equals(actionId)) { + IssueManagement issueManagement = mavenProject.getIssueManagement(); + if(issueManagement != null) { + url = issueManagement.getUrl(); + } + if(url == null) { + openDialog(Messages.OpenUrlAction_error_no_issues); + } + } else if(ID_SCM.equals(actionId)) { + Scm scm = mavenProject.getScm(); + if(scm != null) { + url = scm.getUrl(); + } + if(url == null) { + openDialog(Messages.OpenUrlAction_error_no_scm); + } + } else if(ID_CI.equals(actionId)) { + CiManagement ciManagement = mavenProject.getCiManagement(); + if(ciManagement != null) { + url = ciManagement.getUrl(); + } + if(url == null) { + openDialog(Messages.OpenUrlAction_error_no_ci); + } + } + return url; + } + + private static MavenProject getMavenProject(String groupId, String artifactId, String version, IProgressMonitor monitor) throws Exception { + String name = groupId + ":" + artifactId + ":" + version; + + MavenPlugin plugin = MavenPlugin.getDefault(); + IMaven maven = MavenPlugin.getDefault().getMaven(); + + IMavenProjectFacade projectFacade = plugin.getMavenProjectManager().getMavenProject(groupId, artifactId, version); + if(projectFacade != null) { + return projectFacade.getMavenProject(monitor); + } + + List<ArtifactRepository> artifactRepositories = maven.getArtifactRepositories(); + + Artifact a = maven.resolve(groupId, artifactId, version, "pom", null, artifactRepositories, monitor); //$NON-NLS-1$ + + File pomFile = a.getFile(); + if(pomFile == null) { + openDialog(NLS.bind(Messages.OpenUrlAction_error_open, name)); + return null; + } + + return maven.readProject(pomFile, monitor); + } + + private static void openDialog(final String msg) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + MessageDialog.openInformation(Display.getDefault().getActiveShell(), // + Messages.OpenUrlAction_browser_title, msg); + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow) + */ + public void init(IWorkbenchWindow window) { + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) + */ + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + if(data != null) { + actionId = (String) data; + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/RefreshMavenModelsAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/RefreshMavenModelsAction.java new file mode 100644 index 00000000..5c2fa7fa --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/RefreshMavenModelsAction.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.IWorkingSet; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.MavenUpdateRequest; + + +public class RefreshMavenModelsAction implements IWorkbenchWindowActionDelegate, IExecutableExtension { + + public static final String ID = "org.eclipse.m2e.refreshMavenModelsAction"; //$NON-NLS-1$ + + public static final String ID_SNAPSHOTS = "org.eclipse.m2e.refreshMavenSnapshotsAction"; //$NON-NLS-1$ + + private boolean updateSnapshots = false; + + private boolean offline = false; // should respect global settings + + private IStructuredSelection selection; + + public RefreshMavenModelsAction() { + } + + public RefreshMavenModelsAction(boolean updateSnapshots) { + this.updateSnapshots = updateSnapshots; + } + + // IExecutableExtension + + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + if("snapshots".equals(data)) { //$NON-NLS-1$ + this.updateSnapshots = true; + } + } + + // IWorkbenchWindowActionDelegate + + public void run(IAction action) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + projectManager.refresh(new MavenUpdateRequest(getProjects(), offline, updateSnapshots)); + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } else { + this.selection = null; + } + } + + public void dispose() { + } + + public void init(IWorkbenchWindow window) { + } + + private IProject[] getProjects() { + ArrayList<IProject> projectList = new ArrayList<IProject>(); + if(selection != null) { + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + Object o = it.next(); + if(o instanceof IProject) { + projectList.add((IProject) o); + } else if(o instanceof IWorkingSet) { + IWorkingSet workingSet = (IWorkingSet) o; + for(IAdaptable adaptable : workingSet.getElements()) { + IProject project = (IProject) adaptable.getAdapter(IProject.class); + try { + if(project != null && project.isAccessible() && project.hasNature(IMavenConstants.NATURE_ID)) { + projectList.add(project); + } + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + if(projectList.isEmpty()) { + return ResourcesPlugin.getWorkspace().getRoot().getProjects(); + } + return projectList.toArray(new IProject[projectList.size()]); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/SelectionUtil.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/SelectionUtil.java new file mode 100644 index 00000000..7955c654 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/SelectionUtil.java @@ -0,0 +1,360 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IStorageEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkingSet; +import org.eclipse.ui.IWorkingSetManager; +import org.eclipse.ui.PlatformUI; + +import org.codehaus.plexus.util.IOUtil; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.util.Util; +import org.eclipse.m2e.core.util.Util.FileStoreEditorInputStub; +import org.eclipse.m2e.model.edit.pom.Dependency; + + +/** + * Helper methods to deal with workspace resources passed as navigator selection to actions and wizards. + */ +public class SelectionUtil { + + public static final int UNSUPPORTED = 0; + + public static final int PROJECT_WITH_NATURE = 1; + + public static final int PROJECT_WITHOUT_NATURE = 2; + + public static final int POM_FILE = 4; + + public static final int JAR_FILE = 8; + + public static final int WORKING_SET = 16; + + /** Checks which type the given selection belongs to. */ + public static int getSelectionType(IStructuredSelection selection) { + int type = UNSUPPORTED; + if(selection != null) { + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + int elementType = getElementType(it.next()); + if(elementType == UNSUPPORTED) { + return UNSUPPORTED; + } + type |= elementType; + } + } + return type; + } + + /** Checks which type the given element belongs to. */ + public static int getElementType(Object element) { + IProject project = getType(element, IProject.class); + if(project != null) { + try { + if(project.hasNature(IMavenConstants.NATURE_ID)) { + return PROJECT_WITH_NATURE; + } + return PROJECT_WITHOUT_NATURE; + } catch(CoreException e) { + // ignored + } + } + + IFile file = getType(element, IFile.class); + if(file != null) { + if(IMavenConstants.POM_FILE_NAME.equals(file.getFullPath().lastSegment())) { + return POM_FILE; + } + } + + ArtifactKey artifactKey = getType(element, ArtifactKey.class); + if(artifactKey != null) { + return JAR_FILE; + } + + IWorkingSet workingSet = getType(element, IWorkingSet.class); + if(workingSet!=null) { + return WORKING_SET; + } + + return UNSUPPORTED; + } + + /** + * Checks if the object belongs to a given type and returns it or a suitable adapter. + */ + @SuppressWarnings("unchecked") + public static <T> T getType(Object element, Class<T> type) { + if(element==null) { + return null; + } + if(type.isInstance(element)) { + return (T) element; + } + if(element instanceof IAdaptable) { + T adapter = (T) ((IAdaptable) element).getAdapter(type); + if(adapter != null) { + return adapter; + } + } + return (T) Platform.getAdapterManager().getAdapter(element, type); + } + + public static IPath getSelectedLocation(IStructuredSelection selection) { + Object element = selection == null ? null : selection.getFirstElement(); + + IPath path = getType(element, IPath.class); + if(path != null) { + return path; + } + + IResource resource = getType(element, IResource.class); + if(resource != null) { + return resource.getLocation(); + } + +// IPackageFragmentRoot fragment = getType(element, IResource.class); +// if(fragment != null) { +// IJavaProject javaProject = fragment.getJavaProject(); +// if(javaProject != null) { +// IResource resource = getType(javaProject, IResource.class); +// if(resource != null) { +// return resource.getProject().getProject().getLocation(); +// } +// } +// } + + return null; + } + + public static IWorkingSet getSelectedWorkingSet(IStructuredSelection selection) { + Object element = selection == null ? null : selection.getFirstElement(); + { + IWorkingSet workingSet = getType(element, IWorkingSet.class); + if(workingSet != null) { + return workingSet; + } + } + { + IResource resource = getType(element, IResource.class); + if(resource != null) { + return getWorkingSet(resource.getProject()); + } + } + +// IResource resource = getType(element, IResource.class); +// if(resource != null) { +// return getWorkingSet(resource); +// } + +// IPackageFragmentRoot fragment = getType(element, IPackageFragmentRoot.class); +// if(fragment != null) { +// IJavaProject javaProject = fragment.getJavaProject(); +// if(javaProject != null) { +// IResource resource = getType(javaProject, IResource.class); +// if(resource != null) { +// return getWorkingSet(resource.getProject()); +// } +// } +// } + + return null; + } + + public static IWorkingSet getWorkingSet(Object element) { + IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager(); + for(IWorkingSet workingSet : workingSetManager.getWorkingSets()) { + for(IAdaptable adaptable : workingSet.getElements()) { + if(adaptable.getAdapter(IResource.class) == element) { + return workingSet; + } + } + } + return null; + } + + public static IWorkingSet[] getAssignedWorkingSets(Object element) { + List<IWorkingSet> list = new ArrayList<IWorkingSet>(); + IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager(); + for(IWorkingSet workingSet : workingSetManager.getWorkingSets()) { + for(IAdaptable adaptable : workingSet.getElements()) { + if(adaptable.getAdapter(IResource.class) == element) { + list.add(workingSet); + } + } + } + return list.toArray(new IWorkingSet[list.size()]); + } + + public static ArtifactKey getArtifactKey(Object element) throws CoreException { + if(element instanceof Artifact) { + return new ArtifactKey(((Artifact) element)); + + } else if(element instanceof org.sonatype.aether.graph.DependencyNode) { + org.sonatype.aether.artifact.Artifact artifact = ((org.sonatype.aether.graph.DependencyNode) element) + .getDependency().getArtifact(); + return new ArtifactKey(artifact); + + } else if(element instanceof Dependency) { + Dependency dependency = (Dependency) element; + String groupId = dependency.getGroupId(); + String artifactId = dependency.getArtifactId(); + String version = dependency.getVersion(); + + if(version == null) { + //mkleint: this looks scary + IEditorPart editor = getActiveEditor(); + if(editor!=null) { + MavenProject mavenProject = getMavenProject(editor.getEditorInput(), null); + if(mavenProject!=null) { + Artifact a = mavenProject.getArtifactMap().get(groupId + ":" + artifactId); //$NON-NLS-1$ + version = a.getBaseVersion(); + } + } + } + return new ArtifactKey(dependency.getGroupId(), dependency.getArtifactId(), version, null); + } + + return SelectionUtil.getType(element, ArtifactKey.class); + } + + public static MavenProject getMavenProject(IEditorInput editorInput, IProgressMonitor monitor) throws CoreException { + if(editorInput instanceof IFileEditorInput) { + IFile pomFile = ((IFileEditorInput) editorInput).getFile(); + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + IMavenProjectFacade facade = projectManager.create(pomFile, true, monitor); + if(facade!=null) { + return facade.getMavenProject(monitor); + } + + } else if(editorInput instanceof IStorageEditorInput) { + IStorageEditorInput storageInput = (IStorageEditorInput) editorInput; + IStorage storage = storageInput.getStorage(); + IPath path = storage.getFullPath(); + if(path == null || !new File(path.toOSString()).exists()) { + File tempPomFile = null; + InputStream is = null; + OutputStream os = null; + try { + tempPomFile = File.createTempFile("maven-pom", ".pom"); //$NON-NLS-1$ //$NON-NLS-2$ + os = new FileOutputStream(tempPomFile); + is = storage.getContents(); + IOUtil.copy(is, os); + return readMavenProject(tempPomFile, monitor); + } catch(IOException ex) { + MavenLogger.log("Can't close stream", ex); + } finally { + IOUtil.close(is); + IOUtil.close(os); + if(tempPomFile != null) { + tempPomFile.delete(); + } + } + } else { + return readMavenProject(path.toFile(), monitor); + } + + } else if(editorInput.getClass().getName().endsWith("FileStoreEditorInput")) { //$NON-NLS-1$ + return readMavenProject(new File(Util.proxy(editorInput, FileStoreEditorInputStub.class).getURI().getPath()), monitor); + } + + return null; + } + + private static MavenProject readMavenProject(File pomFile, IProgressMonitor monitor) throws CoreException { + if(monitor==null) { + monitor = new NullProgressMonitor(); + } + + IMaven maven = MavenPlugin.getDefault().getMaven(); + + MavenExecutionRequest request = maven.createExecutionRequest(monitor); + request.setOffline(false); + request.setUpdateSnapshots(false); + request.setRecursive(false); + request.setPom(pomFile); + + MavenExecutionResult result = maven.execute(request, monitor); + + MavenProject project = result.getProject(); + if(project!=null) { + return project; + } + + if(result.hasExceptions()) { + List<IStatus> statuses = new ArrayList<IStatus>(); + List<Throwable> exceptions = result.getExceptions(); + for(Throwable e : exceptions) { + statuses.add(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, e.getMessage(), e)); + } + + throw new CoreException(new MultiStatus(IMavenConstants.PLUGIN_ID, IStatus.ERROR, // + statuses.toArray(new IStatus[statuses.size()]), Messages.SelectionUtil_error_cannot_read, null)); + } + + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, // + Messages.SelectionUtil_error_cannot_read, null)); + } + + private static IEditorPart getActiveEditor() { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if(window != null) { + IWorkbenchPage page = window.getActivePage(); + if(page != null) { + return page.getActiveEditor(); + } + } + return null; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/UpdateConfigurationAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/UpdateConfigurationAction.java new file mode 100644 index 00000000..e8adb704 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/actions/UpdateConfigurationAction.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.actions; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkingSet; +import org.eclipse.ui.progress.IProgressConstants; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.util.M2EUtils; + + +public class UpdateConfigurationAction implements IObjectActionDelegate { + + public static final String ID = "org.eclipse.m2e.updateConfigurationAction"; //$NON-NLS-1$ + + private IStructuredSelection selection; + + private Shell shell; + + public UpdateConfigurationAction(Shell shell) { + this.shell = shell; + } + + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + public void selectionChanged(IAction action, ISelection selection) { + if(selection instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) selection; + } else { + this.selection = null; + } + } + + public void run(IAction action) { + final Set<IProject> projects = getProjects(); + final MavenPlugin plugin = MavenPlugin.getDefault(); + WorkspaceJob job = new WorkspaceJob(Messages.UpdateSourcesAction_job_update_conf) { + public IStatus runInWorkspace(IProgressMonitor monitor) { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + monitor.beginTask(getName(), projects.size()); + + MavenConsole console = plugin.getConsole(); + + long l1 = System.currentTimeMillis(); + console.logMessage("Update started"); + + MultiStatus status = null; + //project names to the errors encountered when updating them + Map<String, Throwable> updateErrors = new HashMap<String, Throwable>(); + + for(IProject project : projects) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + monitor.subTask(project.getName()); + IMavenProjectFacade projectFacade = plugin.getMavenProjectManager().create(project, monitor); + if(projectFacade != null) { + try { + plugin.getProjectConfigurationManager().updateProjectConfiguration(project, // + projectFacade.getResolverConfiguration(), // + new SubProgressMonitor(monitor, 1)); + } catch(CoreException ex) { + if(status == null) { + status = new MultiStatus(IMavenConstants.PLUGIN_ID, IStatus.ERROR, // + Messages.UpdateSourcesAction_error_cannot_update, null); + } + status.add(ex.getStatus()); + updateErrors.put(project.getName(), ex); + } catch(IllegalArgumentException e) { + status = new MultiStatus(IMavenConstants.PLUGIN_ID, IStatus.ERROR, // + Messages.UpdateSourcesAction_error_cannot_update, null); + updateErrors.put(project.getName(), e); + } + } + } + if(updateErrors.size() > 0) { + M2EUtils.showErrorsForProjectsDialog(shell, Messages.UpdateSourcesAction_error_title, + Messages.UpdateSourcesAction_error_message, updateErrors); + } + long l2 = System.currentTimeMillis(); + console.logMessage(NLS.bind("Update completed: {0} sec", ((l2 - l1) / 1000))); + + return status != null ? status : Status.OK_STATUS; + } + }; + // We need to grab workspace lock because IJavaProject.setRawClasspath() needs it. + job.setRule(plugin.getProjectConfigurationManager().getRule()); + job.schedule(); + } + + private Set<IProject> getProjects() { + Set<IProject> projects = new LinkedHashSet<IProject>(); + if(selection != null) { + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + Object element = it.next(); + if(element instanceof IProject) { + projects.add((IProject) element); + } else if(element instanceof IWorkingSet) { + IWorkingSet workingSet = (IWorkingSet) element; + for(IAdaptable adaptable : workingSet.getElements()) { + IProject project = (IProject) adaptable.getAdapter(IProject.class); + try { + if(project != null && project.isAccessible() && project.hasNature(IMavenConstants.NATURE_ID)) { + projects.add(project); + } + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } else if(element instanceof IAdaptable) { + IProject project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); + if(project != null) { + projects.add(project); + } + } + } + } + return projects; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogFactory.java new file mode 100644 index 00000000..8b9331bd --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogFactory.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.archetype; + +import java.util.Properties; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +import org.apache.maven.archetype.Archetype; +import org.apache.maven.archetype.catalog.ArchetypeCatalog; +import org.apache.maven.archetype.source.ArchetypeDataSource; +import org.apache.maven.archetype.source.ArchetypeDataSourceException; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Abstract ArchetypeCatalog factory + */ +public abstract class ArchetypeCatalogFactory { + private final String id; + + private final String description; + + private final boolean editable; + + public ArchetypeCatalogFactory(String id, String description, boolean editable) { + this.id = id; + this.description = description; + this.editable = editable; + } + + public String getId() { + return this.id; + } + + public String getDescription() { + return this.description; + } + + public boolean isEditable() { + return editable; + } + + public abstract ArchetypeCatalog getArchetypeCatalog() throws CoreException; + + public String toString() { + return getId(); + } + + protected Archetype getArchetyper() { + return MavenPlugin.getDefault().getArchetype(); + } + + /** + * Factory for Nexus Indexer ArchetypeCatalog + */ + public static class NexusIndexerCatalogFactory extends ArchetypeCatalogFactory { + public static final String ID = "nexusIndexer"; //$NON-NLS-1$ + + public NexusIndexerCatalogFactory() { + super(ID, Messages.ArchetypeCatalogFactory_indexer_catalog, false); + } + + public ArchetypeCatalog getArchetypeCatalog() throws CoreException { + try { + ArchetypeDataSource source = MavenPlugin.getDefault().getArchetypeDataSource("nexus"); //$NON-NLS-1$ + return source.getArchetypeCatalog(new Properties()); + } catch(ArchetypeDataSourceException ex) { + String msg = NLS.bind(Messages.ArchetypeCatalogFactory_error_missing_catalog, ex.getMessage()); + MavenLogger.log(msg, ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, ex)); + } + } + + } + + /** + * Factory for internal ArchetypeCatalog + */ + public static class InternalCatalogFactory extends ArchetypeCatalogFactory { + public static final String ID = "internal"; //$NON-NLS-1$ + + public InternalCatalogFactory() { + super(ID, Messages.ArchetypeCatalogFactory_internal, false); + } + + public ArchetypeCatalog getArchetypeCatalog() { + return getArchetyper().getInternalCatalog(); + } + } + + /** + * Factory for default local ArchetypeCatalog + */ + public static class DefaultLocalCatalogFactory extends ArchetypeCatalogFactory { + public static final String ID = "defaultLocal"; //$NON-NLS-1$ + + public DefaultLocalCatalogFactory() { + super(ID, Messages.ArchetypeCatalogFactory_default_local, false); + } + + public ArchetypeCatalog getArchetypeCatalog() { + return getArchetyper().getDefaultLocalCatalog(); + } + } + + /** + * Factory for local ArchetypeCatalog + */ + public static class LocalCatalogFactory extends ArchetypeCatalogFactory { + + public LocalCatalogFactory(String path, String description, boolean editable) { + super(path, description == null || description.trim().length() == 0 ? NLS.bind(Messages.ArchetypeCatalogFactory_local, path) : description, editable); + } + + public ArchetypeCatalog getArchetypeCatalog() { + return getArchetyper().getLocalCatalog(getId()); + } + } + + /** + * Factory for remote ArchetypeCatalog + */ + public static class RemoteCatalogFactory extends ArchetypeCatalogFactory { + + private String repositoryUrl = null; + + public RemoteCatalogFactory(String url, String description, boolean editable) { + super(url, description == null || description.trim().length() == 0 ? NLS.bind(Messages.ArchetypeCatalogFactory_remote, url) : description, editable); + repositoryUrl = parseCatalogUrl(url); + } + + /** + * @param url + * @return + + */ + private String parseCatalogUrl(String url) { + if (url == null) { + return null; + } + int length = url.length(); + if (length > 1 && url.endsWith("/")) //$NON-NLS-1$ + { + return url.substring(0, url.length()-1); + } + int idx = url.lastIndexOf("/"); //$NON-NLS-1$ + idx = (idx>0)?idx:0; + if (url.lastIndexOf(".") >= idx) { //$NON-NLS-1$ + //Assume last fragment of the url is a file, let's keep its parent folder + return url.substring(0, idx); + } + return url; + } + + public ArchetypeCatalog getArchetypeCatalog() { + return getArchetyper().getRemoteCatalog(getId()); + } + + + /** + * @return the url of the remote repository hosting the catalog + */ + public String getRepositoryUrl() { + return repositoryUrl; + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogsWriter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogsWriter.java new file mode 100644 index 00000000..aecc0a6d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeCatalogsWriter.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.archetype; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamResult; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLFilterImpl; + +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.LocalCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.RemoteCatalogFactory; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Archetype catalogs writer + * + * @author Eugene Kuleshov + */ +public class ArchetypeCatalogsWriter { + + private static final String ELEMENT_CATALOGS = "archetypeCatalogs"; //$NON-NLS-1$ + + private static final String ELEMENT_CATALOG = "catalog"; //$NON-NLS-1$ + + private static final String ATT_CATALOG_TYPE = "type"; //$NON-NLS-1$ + + private static final String ATT_CATALOG_LOCATION = "location"; //$NON-NLS-1$ + + public static final String ATT_CATALOG_DESCRIPTION = "description"; //$NON-NLS-1$ + + private static final String TYPE_LOCAL = "local"; //$NON-NLS-1$ + + private static final String TYPE_REMOTE = "remote"; //$NON-NLS-1$ + + + public Collection<ArchetypeCatalogFactory> readArchetypeCatalogs(InputStream is) throws IOException { + Collection<ArchetypeCatalogFactory> catalogs = new ArrayList<ArchetypeCatalogFactory>(); + try { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + SAXParser parser = parserFactory.newSAXParser(); + parser.parse(is, new ArchetypeCatalogsContentHandler(catalogs)); + } catch(SAXException ex) { + String msg = Messages.ArchetypeCatalogsWriter_error_parse; + MavenLogger.log(msg, ex); + throw new IOException(NLS.bind(msg, ex.getMessage())); + } catch(ParserConfigurationException ex) { + String msg = Messages.ArchetypeCatalogsWriter_error_parse; + MavenLogger.log(msg, ex); + throw new IOException(NLS.bind(msg, ex.getMessage())); + } + return catalogs; + } + + public void writeArchetypeCatalogs(final Collection<ArchetypeCatalogFactory> catalogs, OutputStream os) throws IOException { + try { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.transform(new SAXSource(new XMLArchetypeCatalogsWriter(catalogs), new InputSource()), new StreamResult(os)); + + } catch(TransformerFactoryConfigurationError ex) { + throw new IOException(NLS.bind(Messages.ArchetypeCatalogsWriter_error_write, ex.getMessage())); + + } catch(TransformerException ex) { + throw new IOException(NLS.bind(Messages.ArchetypeCatalogsWriter_error_write, ex.getMessage())); + + } + } + + static class XMLArchetypeCatalogsWriter extends XMLFilterImpl { + + private final Collection<ArchetypeCatalogFactory> catalogs; + + public XMLArchetypeCatalogsWriter(Collection<ArchetypeCatalogFactory> catalogs) { + this.catalogs = catalogs; + } + + public void parse(InputSource input) throws SAXException { + ContentHandler handler = getContentHandler(); + handler.startDocument(); + handler.startElement(null, ELEMENT_CATALOGS, ELEMENT_CATALOGS, new AttributesImpl()); + + for(ArchetypeCatalogFactory factory : this.catalogs) { + if(factory.isEditable()) { + if(factory instanceof LocalCatalogFactory) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute(null, ATT_CATALOG_TYPE, ATT_CATALOG_TYPE, null, TYPE_LOCAL); + attrs.addAttribute(null, ATT_CATALOG_LOCATION, ATT_CATALOG_LOCATION, null, factory.getId()); + attrs.addAttribute(null, ATT_CATALOG_DESCRIPTION, ATT_CATALOG_DESCRIPTION, null, factory.getDescription()); + handler.startElement(null, ELEMENT_CATALOG, ELEMENT_CATALOG, attrs); + handler.endElement(null, ELEMENT_CATALOG, ELEMENT_CATALOG); + } else if(factory instanceof RemoteCatalogFactory) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute(null, ATT_CATALOG_TYPE, ATT_CATALOG_TYPE, null, TYPE_REMOTE); + attrs.addAttribute(null, ATT_CATALOG_LOCATION, ATT_CATALOG_LOCATION, null, factory.getId()); + attrs.addAttribute(null, ATT_CATALOG_DESCRIPTION, ATT_CATALOG_DESCRIPTION, null, factory.getDescription()); + handler.startElement(null, ELEMENT_CATALOG, ELEMENT_CATALOG, attrs); + handler.endElement(null, ELEMENT_CATALOG, ELEMENT_CATALOG); + } + } + } + + handler.endElement(null, ELEMENT_CATALOGS, ELEMENT_CATALOGS); + handler.endDocument(); + } + } + + static class ArchetypeCatalogsContentHandler extends DefaultHandler { + + private Collection<ArchetypeCatalogFactory> catalogs; + + public ArchetypeCatalogsContentHandler(Collection<ArchetypeCatalogFactory> catalogs) { + this.catalogs = catalogs; + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) { + if(ELEMENT_CATALOG.equals(qName) && attributes != null) { + String type = attributes.getValue(ATT_CATALOG_TYPE); + if(TYPE_LOCAL.equals(type)) { + String path = attributes.getValue(ATT_CATALOG_LOCATION); + if(path!=null) { + String description = attributes.getValue(ATT_CATALOG_DESCRIPTION); + catalogs.add(new LocalCatalogFactory(path, description, true)); + } + } else if(TYPE_REMOTE.equals(type)) { + String url = attributes.getValue(ATT_CATALOG_LOCATION); + if(url!=null) { + String description = attributes.getValue(ATT_CATALOG_DESCRIPTION); + catalogs.add(new RemoteCatalogFactory(url, description, true)); + } + } + } + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeManager.java new file mode 100644 index 00000000..e447e2de --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/archetype/ArchetypeManager.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.archetype; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; + +import org.codehaus.plexus.util.IOUtil; + +import org.apache.maven.archetype.catalog.Archetype; + + + + +/** + * Archetype Manager + * + * @author Eugene Kuleshov + */ +public class ArchetypeManager { + + private final Map<String, ArchetypeCatalogFactory> catalogs = new LinkedHashMap<String, ArchetypeCatalogFactory>(); + + private final File configFile; + + private final ArchetypeCatalogsWriter writer; + + public ArchetypeManager(File configFile) { + this.configFile = configFile; + this.writer = new ArchetypeCatalogsWriter(); + } + + /** + * @return Collection of ArchetypeCatalogFactory + */ + public Collection<ArchetypeCatalogFactory> getArchetypeCatalogs() { + return new ArrayList<ArchetypeCatalogFactory>(catalogs.values()); + } + + public void addArchetypeCatalogFactory(ArchetypeCatalogFactory factory) { + if(factory != null) { + catalogs.put(factory.getId(), factory); + } + } + + public void removeArchetypeCatalogFactory(String catalogId) { + catalogs.remove(catalogId); + } + + public ArchetypeCatalogFactory getArchetypeCatalogFactory(String catalogId) { + return catalogs.get(catalogId); + } + + public void readCatalogs() throws IOException { + if(configFile.exists()) { + InputStream is = null; + try { + is = new FileInputStream(configFile); + Collection<ArchetypeCatalogFactory> catalogs = writer.readArchetypeCatalogs(is); + for(Iterator<ArchetypeCatalogFactory> it = catalogs.iterator(); it.hasNext();) { + addArchetypeCatalogFactory(it.next()); + } + } finally { + IOUtil.close(is); + } + } + } + + public void saveCatalogs() throws IOException { + OutputStream os = null; + try { + os = new FileOutputStream(configFile); + writer.writeArchetypeCatalogs(getArchetypeCatalogs(), os); + } finally { + IOUtil.close(os); + } + } + + /** + * @return the archetypeCatalogFactory containing the archetype parameter, null if none was found. + */ + public <T extends ArchetypeCatalogFactory> T findParentCatalogFactory(Archetype a, Class<T> type) throws CoreException { + if (a!=null){ + for (ArchetypeCatalogFactory factory : getArchetypeCatalogs()) { + if ((type.isAssignableFrom(factory.getClass())) + //temporary hack to get around https://issues.sonatype.org/browse/MNGECLIPSE-1792 + //cf. MavenProjectWizardArchetypePage.getAllArchetypes + && !(factory.getDescription() != null && factory.getDescription().startsWith("Test")) //$NON-NLS-1$ + && factory.getArchetypeCatalog().getArchetypes().contains(a)) { + return (T)factory; + } + } + } + return null; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/AbstractEclipseBuildContext.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/AbstractEclipseBuildContext.java new file mode 100644 index 00000000..df3a519d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/AbstractEclipseBuildContext.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.builder; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.sonatype.plexus.build.incremental.BuildContext; + + +/** + * AbstractEclipseBuildContext + * + * @author igor + */ +public abstract class AbstractEclipseBuildContext implements BuildContext { + + public static class Message { + public final File file; + + public final int line; + + public final int column; + + public final String message; + + public final Throwable cause; + + Message(File file, int line, int column, String message, Throwable cause) { + this.file = file; + this.line = line; + this.column = column; + this.message = message; + this.cause = cause; + } + } + + private final Set<File> refresh = new HashSet<File>(); + + protected final Map<String, Object> context; + + private final ArrayList<Message> errorMessages = new ArrayList<Message>(); + + private final ArrayList<Message> warningMessages = new ArrayList<Message>(); + + protected AbstractEclipseBuildContext(Map<String, Object> context) { + this.context = context; + } + + public void refresh(File file) { + refresh.add(file); + } + + public Set<File> getFiles() { + return refresh; + } + + public OutputStream newFileOutputStream(File file) throws IOException { + return new ChangedFileOutputStream(file, this); + } + + /** + * Returns path relative to delta resource location. + */ + protected IPath getRelativePath(File file) { + IPath basepath = getBaseResource().getLocation(); + IPath path = Path.fromOSString(file.getAbsolutePath()); + + if(!basepath.isPrefixOf(path)) { + return null; + } + + return path.removeFirstSegments(basepath.segmentCount()); + } + + protected IResource getResource(File file) { + IPath relpath = getRelativePath(file); + if (relpath == null) { + return null; + } + IResource baseResource = getBaseResource(); + if (baseResource instanceof IContainer) { + return ((IContainer) baseResource).findMember(relpath); + } + return null; + } + + protected abstract IResource getBaseResource(); + + public void setValue(String key, Object value) { + context.put(key, value); + } + + public Object getValue(String key) { + return context.get(key); + } + + public void addError(File file, int line, int column, String message, Throwable cause) { + errorMessages.add(new Message(file, line, column, message, cause)); + } + + public void addWarning(File file, int line, int column, String message, Throwable cause) { + warningMessages.add(new Message(file, line, column, message, cause)); + } + + public List<Message> getErrorMessages() { + return errorMessages; + } + + public List<Message> getWarningMessages() { + return warningMessages; + } + + public boolean isUptodate(File target, File source) { + IResource targetResource = getResource(target); + IResource sourceResource = getResource(source); + return targetResource != null && targetResource.isAccessible() && !hasDelta(target) + && sourceResource != null && sourceResource.isAccessible() && !hasDelta(source) + && targetResource.getLocalTimeStamp() >= sourceResource.getLocalTimeStamp(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ChangedFileOutputStream.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ChangedFileOutputStream.java new file mode 100644 index 00000000..51aa0093 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ChangedFileOutputStream.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.builder; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.sonatype.plexus.build.incremental.BuildContext; + +/** + * Writes to the file only if content of the file is different. + * + * TODO. Current implementation defers actual writing to the output file until + * invocation of {@link #close()} method. This results in missed/ignored IOExceptions + * in some cases. First, {@link #flush()} method does not actually flush buffer to the disk. + * Second, any problems writing to the file will be reported as IOException + * thrown by {@link #close()}, which are generally ignored. + */ +public class ChangedFileOutputStream extends OutputStream { + + private final File file; + private final BuildContext buildContext; + private final OutputStream os; + + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + public ChangedFileOutputStream(File file) throws FileNotFoundException { + this(file, null); + } + + public ChangedFileOutputStream(File file, BuildContext buildContext) throws FileNotFoundException { + this.file = file; + this.buildContext = buildContext; + this.os = new BufferedOutputStream(new FileOutputStream(file)); + } + + public void write(int b) { + buffer.write(b); + } + + public void write(byte[] b, int off, int len) { + buffer.write(b, off, len); + } + + public void close() throws IOException { + try { + writeIfNewOrChanged(); + } finally { + os.close(); + } + } + + protected void writeIfNewOrChanged() throws IOException { + byte[] bytes = buffer.toByteArray(); + + boolean needToWrite = false; + + // XXX harden + if (file.exists()) { + BufferedInputStream is = new BufferedInputStream(new FileInputStream(file)); + try { + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] != is.read()) { + needToWrite = true; + break; + } + } + } finally { + try { + is.close(); + } catch (IOException e) { + + } + } + } else { + // file does not exist + needToWrite = true; + } + + if (needToWrite) { + if (buildContext != null) { + buildContext.refresh(file); + } + + os.write(bytes); + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseBuildContext.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseBuildContext.java new file mode 100644 index 00000000..c5161f7d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseBuildContext.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.builder; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; + +import org.codehaus.plexus.util.Scanner; + +import org.sonatype.plexus.build.incremental.EmptyScanner; + +import org.eclipse.m2e.core.internal.builder.ResourceScanner; + +/** + * EclipseBuildContext + * + * @author igor + */ +public class EclipseBuildContext extends AbstractEclipseBuildContext { + + protected final IProject project; + + public EclipseBuildContext(IProject project, Map<String, Object> context) { + super(context); + this.project = project; + } + + public boolean hasDelta(String relpath) { + return true; + } + + @SuppressWarnings("rawtypes") + public boolean hasDelta(List relpath) { + return true; + } + + public boolean hasDelta(File file) { + return true; + } + + public Scanner newDeleteScanner(File basedir) { + return new EmptyScanner(basedir); + } + + public Scanner newScanner(File basedir) { + IPath relpath = getRelativePath(basedir); + return new ResourceScanner(project.findMember(relpath)); + } + + public Scanner newScanner(File basedir, boolean ignoreDelta) { + return newScanner(basedir); + } + + protected IProject getBaseResource() { + return project; + } + + public boolean isIncremental() { + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseIncrementalBuildContext.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseIncrementalBuildContext.java new file mode 100644 index 00000000..e5f039fb --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/EclipseIncrementalBuildContext.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.builder; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.Scanner; + +import org.sonatype.plexus.build.incremental.EmptyScanner; + +public class EclipseIncrementalBuildContext extends AbstractEclipseBuildContext { + + private final IResourceDelta delta; + + public EclipseIncrementalBuildContext(IResourceDelta delta, Map<String, Object> context) { + super(context); + this.delta = delta; + } + + public boolean hasDelta(String relPath) { + IPath path = new Path(relPath); + return hasDelta(path); + } + + protected boolean hasDelta(IPath path) { + return delta == null || path == null || delta.findMember(path) != null; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public boolean hasDelta(List relPaths) { + for (String relPath : (List<String>) relPaths) { + if (hasDelta(relPath)) { + return true; + } + } + return false; + } + + public boolean hasDelta(File file) { + return hasDelta(getRelativePath(file)); + } + + public Scanner newDeleteScanner(File basedir) { + IResourceDelta reldelta = getDelta(basedir); + + if (reldelta == null || !isRemove(reldelta)) { + return new EmptyScanner(basedir); + } + + return new ResourceDeltaScanner(reldelta, true); + } + + public Scanner newScanner(File basedir) { + return newScanner(basedir, false); + } + + public Scanner newScanner(File basedir, boolean ignoreDelta) { + if (!ignoreDelta) { + IResourceDelta reldelta = getDelta(basedir); + + if (reldelta == null || !isContentChange(reldelta)) { + return new EmptyScanner(basedir); + } + + return new ResourceDeltaScanner(reldelta, false); + } + + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir(basedir); + return ds; + } + + private IResourceDelta getDelta(File file) { + IPath relpath = getRelativePath(file); + if (relpath == null) { + return null; + } + return delta.findMember(relpath); + } + + static boolean isContentChange(IResourceDelta delta) { + int kind = delta.getKind(); + if (IResourceDelta.ADDED == kind) { + return true; + } + + if (IResourceDelta.CHANGED == kind) { + if (delta.getResource() instanceof IContainer) { + return true; + } + + int flags = delta.getFlags(); + + return (flags & IResourceDelta.CONTENT) != 0; + } + + return false; + } + + static boolean isRemove(IResourceDelta delta) { + int kind = delta.getKind(); + + if (IResourceDelta.REMOVED == kind) { + return true; + } + + if (IResourceDelta.CHANGED == kind && delta.getResource() instanceof IContainer) { + return true; + } + + return false; + } + + + @Override + protected IResource getBaseResource() { + return delta.getResource(); + } + + public boolean isIncremental() { + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ResourceDeltaScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ResourceDeltaScanner.java new file mode 100644 index 00000000..092d419d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/builder/ResourceDeltaScanner.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.builder; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.runtime.CoreException; + +import org.codehaus.plexus.util.AbstractScanner; + +public class ResourceDeltaScanner extends AbstractScanner { + + protected final IResourceDelta delta; + + protected final List<String> includedDirectories = new ArrayList<String>(); + + protected final List<String> includedFiles = new ArrayList<String>(); + + protected final boolean deleted; + + public ResourceDeltaScanner(IResourceDelta delta, boolean deleted) { + this.delta = delta; + this.deleted = deleted; + } + + public String[] getIncludedDirectories() { + return includedDirectories.toArray(new String[includedDirectories.size()]); + } + + public String[] getIncludedFiles() { + return includedFiles.toArray(new String[includedFiles.size()]); + } + + public void scan() { + try { + setupDefaultFilters(); + scanDelta(); + } catch(CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void scanDelta() throws CoreException { + delta.accept(new IResourceDeltaVisitor() { + + @SuppressWarnings("synthetic-access") + public boolean visit(IResourceDelta delta) { + String relpath = getRelativePath(delta); + if (isInteresting(delta) && isIncluded(relpath) && !isExcluded(relpath)) { + IResource resource = delta.getResource(); + if (resource instanceof IContainer) { + includedDirectories.add(relpath); + } else { + includedFiles.add(relpath); + } + return true; + } else if (delta.getResource() instanceof IFolder) { + return couldHoldIncluded(relpath); + } + + return false; + } + + }); + } + + protected boolean isInteresting(IResourceDelta delta) { + return deleted? EclipseIncrementalBuildContext.isRemove(delta): EclipseIncrementalBuildContext.isContentChange(delta); + } + + protected String getRelativePath(IResourceDelta delta) { + return delta.getFullPath().removeFirstSegments(this.delta.getFullPath().segmentCount()).toOSString(); + } + + public File getBasedir() { + return delta.getResource().getLocation().toFile(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConsoleListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConsoleListener.java new file mode 100644 index 00000000..1d1a7b93 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConsoleListener.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.core; + +import java.util.EventListener; + +/** + * A console listener is notified of output to the Maven console. + * + * @author Benjamin Bentmann + */ +public interface IMavenConsoleListener extends EventListener { + + void loggingMessage(String msg); + + void loggingError(String msg); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConstants.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConstants.java new file mode 100644 index 00000000..fb9562d1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/IMavenConstants.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.core; + +import org.eclipse.core.runtime.QualifiedName; + +/** + * Maven Constants + * + * @author Eugene Kuleshov + */ +public interface IMavenConstants { + + public static final String PLUGIN_ID = "org.eclipse.m2e.core"; //$NON-NLS-1$ + + public static final String NATURE_ID = PLUGIN_ID + ".maven2Nature"; //$NON-NLS-1$ + + public static final String BUILDER_ID = PLUGIN_ID + ".maven2Builder"; //$NON-NLS-1$ + + public static final String MARKER_ID = PLUGIN_ID + ".maven2Problem"; //$NON-NLS-1$ + + public static final String MARKER_POM_LOADING_ID = MARKER_ID + ".pomloading"; //$NON-NLS-1$ + + public static final String MARKER_CONFIGURATION_ID = MARKER_ID + ".configuration"; //$NON-NLS-1$ + + public static final String MARKER_DEPENDENCY_ID = MARKER_ID + ".dependency"; //$NON-NLS-1$ + + public static final String MARKER_BUILD_ID = MARKER_ID + ".build"; //$NON-NLS-1$ + + /** + * string that gets included in pom.xml file comments and makes the marker manager to ignore + * the managed version override marker + */ + public static final String MARKER_IGNORE_MANAGED = "$NO-MVN-MAN-VER$";//$NON-NLS-1$ + + public static final String MAVEN_COMPONENT_CONTRIBUTORS_XPT = PLUGIN_ID + ".mavenComponentContributors"; //$NON-NLS-1$ + + public static final String POM_FILE_NAME = "pom.xml"; //$NON-NLS-1$ + + public static final String PREFERENCE_PAGE_ID = PLUGIN_ID + ".MavenProjectPreferencePage"; //$NON-NLS-1$ + + public static final String NO_WORKSPACE_PROJECTS = "noworkspace"; //$NON-NLS-1$ + + public static final String ACTIVE_PROFILES = "profiles"; //$NON-NLS-1$ + + public static final String FILTER_RESOURCES = "filterresources"; //$NON-NLS-1$ + + public static final String JAVADOC_CLASSIFIER = "javadoc"; //$NON-NLS-1$ + + public static final String SOURCES_CLASSIFIER = "sources"; //$NON-NLS-1$ + + + /** + * Session property key used to indicate that full maven build was requested for a project. + * It is not intended to be used by clients directly. + */ + public static final QualifiedName FULL_MAVEN_BUILD = new QualifiedName(PLUGIN_ID, "fullBuild"); //$NON-NLS-1$ + + /** + * The name of the folder containing metadata information for the workspace. + */ + public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$ + + public static final String INDEX_UPDATE_PROP = "indexUpdate"; //$NON-NLS-1$ + + public static final String MARKER_ATTR_EDITOR_HINT = "editor_hint"; + + public static final String EDITOR_HINT_PARENT_GROUP_ID = "parent_groupid"; + + public static final String EDITOR_HINT_PARENT_VERSION = "parent_version"; + + public static final String EDITOR_HINT_MANAGED_DEPENDENCY_OVERRIDE = "managed_dependency_override"; + + public static final String EDITOR_HINT_MANAGED_PLUGIN_OVERRIDE = "managed_plugin_override"; + + public static final String EDITOR_HINT_MISSING_SCHEMA = "missing_schema"; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenConsole.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenConsole.java new file mode 100644 index 00000000..a6196d0f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenConsole.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.core; + +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleListener; + +/** + * Maven Console + * + * @author Eugene Kuleshov + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface MavenConsole extends IConsole { + + void logMessage(String msg); + + void logError(String msg); + + IConsoleListener newLifecycle(); + + void shutdown(); + + void showConsole(); + + void closeConsole(); + + void addMavenConsoleListener(IMavenConsoleListener listener); + + void removeMavenConsoleListener(IMavenConsoleListener listener); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenLogger.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenLogger.java new file mode 100644 index 00000000..2cef32db --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/MavenLogger.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.core; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + + +/** + * Maven Logger + * + * @author Eugene Kuleshov + */ +public class MavenLogger { + + private static ILog LOG; + + public static void setLog(ILog log) { + LOG = log; + } + + public static void log(IStatus status) { + LOG.log(status); + } + + public static void log(CoreException ex) { + IStatus s = ex.getStatus(); + if(s.getException() == null) { + int n = s.getSeverity(); + log(new Status(n == IStatus.CANCEL || n == IStatus.ERROR || n == IStatus.INFO // + || n == IStatus.WARNING || n == IStatus.OK ? n : IStatus.ERROR, // + s.getPlugin() == null ? IMavenConstants.PLUGIN_ID : s.getPlugin(), // + s.getCode(), // + s.getMessage() == null ? s.toString() : s.getMessage(), // + ex)); + } else { + log(s); + } + } + + public static void log(String msg, Throwable t) { + log(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, msg, t)); + } + + public static void log(String msg) { + log(new Status(IStatus.OK, IMavenConstants.PLUGIN_ID, msg)); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/Messages.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/Messages.java new file mode 100644 index 00000000..854797f1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/core/Messages.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.core; + +import java.util.MissingResourceException; + +import com.ibm.icu.text.MessageFormat; +import com.ibm.icu.util.ULocale; +import com.ibm.icu.util.UResourceBundle; + +//mkleint: this class looks like not following the default eclipse way of i18n and resides in public packages + +public class Messages { + private static final String BUNDLE_NAME = IMavenConstants.PLUGIN_ID + ".messages"; //$NON-NLS-1$ + + private static final UResourceBundle RESOURCE_BUNDLE = UResourceBundle.getBundleInstance(BUNDLE_NAME, + ULocale.getDefault(), Messages.class.getClassLoader()); + + private Messages() { + } + + public static String getString(String key) { + try { + return RESOURCE_BUNDLE.getString(key); + } catch(MissingResourceException e) { + return '!' + key + '!'; + } + } + + public static String getString( String key, Object[] args ) { + try { + return MessageFormat.format( + RESOURCE_BUNDLE.getString( key ), args ); + } catch( MissingResourceException e ) { + return '!' + key + '!'; + } + } + + public static String getString( String key, Object arg ) { + return getString( key, new Object[]{ arg } ); + } + + public static String getString( String key, int arg ) { + return getString( key, new Object[]{ String.valueOf(arg) } ); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/AbstractMavenConfigurationChangeListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/AbstractMavenConfigurationChangeListener.java new file mode 100644 index 00000000..88d2538c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/AbstractMavenConfigurationChangeListener.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +/** + * AbstractMavenConfigurationChangeListener + * + * @author igor + */ +public abstract class AbstractMavenConfigurationChangeListener implements IMavenConfigurationChangeListener { + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactKey.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactKey.java new file mode 100644 index 00000000..d0697c1f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactKey.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.Serializable; + +import org.eclipse.osgi.util.NLS; + +import org.apache.maven.artifact.Artifact; + +public class ArtifactKey implements Serializable { + private static final long serialVersionUID = -8984509272834024387L; + + private final String groupId; + private final String artifactId; + private final String version; + private final String classifier; + + /** + * Note that this constructor uses Artifact.getBaseVersion + */ + public ArtifactKey(Artifact a) { + this(a.getGroupId(), a.getArtifactId(), a.getBaseVersion(), null); + } + + public ArtifactKey(org.sonatype.aether.artifact.Artifact a) { + this(a.getGroupId(), a.getArtifactId(), a.getBaseVersion(), null); + } + + public ArtifactKey(String groupId, String artifactId, String version, String classifier) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classifier = classifier; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof ArtifactKey) { + ArtifactKey other = (ArtifactKey) o; + return equals(groupId, other.groupId) + && equals(artifactId, other.artifactId) + && equals(version, other.version) + && equals(classifier, other.classifier); + } + return false; + } + + public int hashCode() { + int hash = 17; + hash = hash * 31 + (groupId != null? groupId.hashCode(): 0); + hash = hash * 31 + (artifactId != null? artifactId.hashCode(): 0); + hash = hash * 31 + (version != null? version.hashCode(): 0); + hash = hash * 31 + (classifier != null? classifier.hashCode(): 0); + return hash; + } + + private static boolean equals(Object o1, Object o2) { + return o1 == null? o2 == null: o1.equals(o2); + } + + // XXX this method does not belong here, it compares versions, while ArtifactKey baseVersions + public static boolean equals(Artifact a1, Artifact a2) { + if (a1 == null) { + return a2 == null; + } + if (a2 == null) { + return false; + } + return equals(a1.getGroupId(), a2.getGroupId()) + && equals(a1.getArtifactId(), a2.getArtifactId()) + && equals(a1.getVersion(), a2.getVersion()) + && equals(a1.getClassifier(), a2.getClassifier()); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(groupId).append(':').append(artifactId).append(':').append(version); + if(classifier != null) { + sb.append(':').append(classifier); + } + return sb.toString(); + } + + public static ArtifactKey fromPortableString(String str) { + int p, c; + + p = 0; c = nextColonIndex(str, p); + String groupId = substring(str, p, c); + + p = c + 1; c = nextColonIndex(str, p); + String artifactId = substring(str, p, c); + + p = c + 1; c = nextColonIndex(str, p); + String version = substring(str, p, c); + + p = c + 1; c = nextColonIndex(str, p); + String classifier = substring(str, p, c); + + return new ArtifactKey(groupId, artifactId, version, classifier); + } + + private static String substring(String str, int start, int end) { + String substring = str.substring(start, end); + return "".equals(substring)? null: substring; //$NON-NLS-1$ + } + + private static int nextColonIndex(String str, int pos) { + int idx = str.indexOf(':', pos); + if (idx < 0) throw new IllegalArgumentException(NLS.bind("Invalid portable string: {0}", str)); + return idx; + } + + public String toPortableString() { + StringBuilder sb = new StringBuilder(); + if (groupId != null) sb.append(groupId); sb.append(':'); + if (artifactId != null) sb.append(artifactId); sb.append(':'); + if (version != null) sb.append(version); sb.append(':'); + if (classifier != null) sb.append(classifier); sb.append(':'); + return sb.toString(); + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getVersion() { + return version; + } + + public String getClassifier() { + return classifier; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRef.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRef.java new file mode 100644 index 00000000..5d5ecb88 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRef.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.Serializable; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; + +/** + * @author Igor Fedorenko + */ +public class ArtifactRef implements Serializable { + private static final long serialVersionUID = -7560496230862532267L; + + private final ArtifactKey artifactKey; + private final String scope; + + public ArtifactRef(Artifact artifact) { + this.artifactKey = new ArtifactKey(artifact); + this.scope = artifact.getScope(); + } + + public ArtifactKey getArtifactKey() { + return artifactKey; + } + + public String getGroupId() { + return artifactKey.getGroupId(); + } + + public String getArtifactId() { + return artifactKey.getArtifactId(); + } + + public String getVersion() { + return artifactKey.getVersion(); + } + + public String getClassifier() { + return artifactKey.getClassifier(); + } + + public String getScope() { + return scope; + } + + public static Set<ArtifactKey> toArtifactKey(Set<ArtifactRef> refs) { + LinkedHashSet<ArtifactKey> keys = new LinkedHashSet<ArtifactKey>(refs.size()); + for (ArtifactRef ref : refs) { + keys.add(ref.getArtifactKey()); + } + return keys; + } + + public static Set<ArtifactRef> fromArtifact(Set<Artifact> artifacts) { + LinkedHashSet<ArtifactRef> refs = new LinkedHashSet<ArtifactRef>(artifacts.size()); + for (Artifact artifact : artifacts) { + refs.add(new ArtifactRef(artifact)); + } + return refs; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRepositoryRef.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRepositoryRef.java new file mode 100644 index 00000000..d5a458ac --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ArtifactRepositoryRef.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.Serializable; + +import org.apache.maven.artifact.repository.ArtifactRepository; + + +public class ArtifactRepositoryRef implements Serializable { + + private static final long serialVersionUID = 8859289246547259912L; + + private final String id; + + private final String url; + + private final String username; + + public ArtifactRepositoryRef(ArtifactRepository repository) { + this.id = repository.getId(); + this.url = repository.getUrl(); + this.username = repository.getAuthentication() != null? repository.getAuthentication().getUsername(): null; + } + + public String getId() { + return id; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public int hashCode() { + int hash = 17; + hash = hash * 31 + (id != null ? id.hashCode() : 0); + hash = hash * 31 + (url != null ? url.hashCode() : 0); + hash = hash * 31 + (username != null ? username.hashCode() : 0); + return hash; + } + + public boolean equals(Object o) { + if(o == this) { + return true; + } + if(!(o instanceof ArtifactRepositoryRef)) { + return false; + } + ArtifactRepositoryRef other = (ArtifactRepositoryRef) o; + return eq(id, other.id) && eq(url, other.url) && eq(username, other.username); + } + + private static <T> boolean eq(T a, T b) { + return a != null? a.equals(b): b == null; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ILocalRepositoryListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ILocalRepositoryListener.java new file mode 100644 index 00000000..5a7d7211 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ILocalRepositoryListener.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.File; + +/** + * ILocalRepositoryListener + * + * @author igor + * + * @provisional This interface is provisional and can be changed or removed without notice + */ +public interface ILocalRepositoryListener { + + /** + * New artifact has been downloaded or installed to maven local repository + */ + public void artifactInstalled(File repositoryBasedir, ArtifactKey artifact, File artifactFile); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java new file mode 100644 index 00000000..651cf9a8 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMaven.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.model.ConfigurationContainer; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.building.SettingsProblem; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.proxy.ProxyInfo; + +/** + * Entry point for all Maven functionality in m2e. Note that this component does not directly support workspace artifact + * resolution. + * + * @author igor + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMaven { + + /** + * Creates new Maven execution request. This method is not long running, but created execution request is configured + * to report progress to provided progress monitor. Monitor can be null. + */ + public MavenExecutionRequest createExecutionRequest(IProgressMonitor monitor) throws CoreException; + + // POM Model read/write operations + + public Model readModel(InputStream in) throws CoreException; + + public Model readModel(File pomFile) throws CoreException; + + public void writeModel(Model model, OutputStream out) throws CoreException; + + // artifact resolution + + /** + * Resolves specified artifact from specified remote repositories. + * @return Artifact resolved artifact + * @throws CoreException if the artifact cannot be resolved. + */ + public Artifact resolve(String groupId, String artifactId, String version, String type, String classifier, + List<ArtifactRepository> artifactRepositories, IProgressMonitor monitor) throws CoreException; + + /** + * Returns path of the specified artifact relative to repository baseDir. Can use used to access local repository + * files bypassing maven resolution logic. + */ + public String getArtifactPath(ArtifactRepository repository, String groupId, String artifactId, String version, + String type, String classifier) throws CoreException; + + /** + * Returns true if the artifact does NOT exist in the local repository and + * known to be UNavailable from all specified repositories. + */ + public boolean isUnavailable(String groupId, String artifactId, String version, String type, String classifier, + List<ArtifactRepository> repositories) throws CoreException; + + // read MavenProject + + public MavenProject readProject(File pomFile, IProgressMonitor monitor) throws CoreException; + + public MavenExecutionResult readProject(MavenExecutionRequest request, IProgressMonitor monitor) throws CoreException; + + /** + * Makes MavenProject instances returned by #readProject methods suitable for caching and reuse with other + * MavenSession instances.<br/> + * Do note that MavenProject.getParentProject() cannot be used for detached MavenProject instances, + * #resolveParentProject to read parent project instance. + */ + public void detachFromSession(MavenProject project) throws CoreException; + + public MavenProject resolveParentProject(MavenExecutionRequest request, MavenProject project, IProgressMonitor monitor) + throws CoreException; + + // execution + + public MavenExecutionResult execute(MavenExecutionRequest request, IProgressMonitor monitor); + + public MavenSession createSession(MavenExecutionRequest request, MavenProject project); + + public void execute(MavenSession session, MojoExecution execution, IProgressMonitor monitor); + + public MavenExecutionPlan calculateExecutionPlan(MavenExecutionRequest request, MavenProject project, + IProgressMonitor monitor) throws CoreException; + + public <T> T getMojoParameterValue(MavenSession session, MojoExecution mojoExecution, String parameter, + Class<T> asType) throws CoreException; + + public <T> T getMojoParameterValue(String parameter, Class<T> type, MavenSession session, Plugin plugin, + ConfigurationContainer configuration, String goal) throws CoreException; + + // configuration + + /** + * TODO should we expose Settings or provide access to servers and proxies instead? + */ + public Settings getSettings() throws CoreException; + + public String getLocalRepositoryPath(); + + public ArtifactRepository getLocalRepository() throws CoreException; + + public void populateDefaults(MavenExecutionRequest request) throws CoreException; + + public ArtifactRepository createArtifactRepository(String id, String url) throws CoreException; + + /** + * Convenience method, fully equivalent to getArtifactRepositories(true) + */ + public List<ArtifactRepository> getArtifactRepositories() throws CoreException; + + /** + * Returns list of remote artifact repositories configured in settings.xml. Only profiles active by default are + * considered when calculating the list. + * + * If injectSettings=true, mirrors, authentication and proxy info will be injected. + * + * If injectSettings=false, raw repository definition will be used. + */ + public List<ArtifactRepository> getArtifactRepositories(boolean injectSettings) throws CoreException; + + public List<ArtifactRepository> getPluginArtifactRepositories() throws CoreException; + + public List<ArtifactRepository> getPluginArtifactRepositories(boolean injectSettings) throws CoreException; + + public Settings buildSettings(String globalSettings, String userSettings) throws CoreException; + + public void writeSettings(Settings settings, OutputStream out) throws CoreException; + + public List<SettingsProblem> validateSettings(String settings); + + public List<Mirror> getMirrors() throws CoreException; + + public Mirror getMirror(ArtifactRepository repo) throws CoreException; + + public void addSettingsChangeListener(ISettingsChangeListener listener); + + public void removeSettingsChangeListener(ISettingsChangeListener listener); + + public void reloadSettings() throws CoreException; + + public Server decryptPassword(Server server) throws CoreException; + + /** + * Temporary solution/workaround for http://jira.codehaus.org/browse/MNG-4194. Extensions realm is created each time + * MavenProject instance is built, so we have to remove unused extensions realms to avoid OOME. + */ + @Deprecated + public void xxxRemoveExtensionsRealm(MavenProject project); + + /** @provisional */ + public void addLocalRepositoryListener(ILocalRepositoryListener listener); + + /** @provisional */ + public void removeLocalRepositoryListener(ILocalRepositoryListener listener); + + /** + * Creates wagon TransferListener that can be used with Archetype, NexusIndexer + * and other components that use wagon API directly. The listener will adopt + * wagon transfer events to corresponding calls to IProgressMonitor and all + * registered ILocalRepositoryListeners. + * + * @deprecated IMaven API should not expose maven.repository.ArtifactTransferListener + */ + public TransferListener createTransferListener(IProgressMonitor monitor); + + public ProxyInfo getProxyInfo(String protocol) throws CoreException; + + /** + * Sort projects by build order + */ + public List<MavenProject> getSortedProjects(List<MavenProject> projects) throws CoreException; + + public String resolvePluginVersion(String groupId, String artifactId, MavenSession session) throws CoreException; + + /** + * Returns new mojo instances configured according to provided mojoExecution. Caller must release returned mojo with + * {@link #releaseMojo(Object, MojoExecution)}. This method is intended to allow introspection of mojo configuration + * parameters, use {@link #execute(MavenSession, MojoExecution, IProgressMonitor)} to execute mojo. + */ + public <T> T getConfiguredMojo(MavenSession session, MojoExecution mojoExecution, Class<T> clazz) throws CoreException; + + /** + * Releases resources used by Mojo acquired with {@link #getConfiguredMojo(MavenSession, MojoExecution, Class)} + */ + void releaseMojo(Object mojo, MojoExecution mojoExecution) throws CoreException; + + /** + * Gets class realm of the specified project. + * + * @return The class realm of the specified project. + */ + ClassLoader getProjectRealm(MavenProject project); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfiguration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfiguration.java new file mode 100644 index 00000000..1aed83b1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfiguration.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + + + +/** + * IMavenConfiguration + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMavenConfiguration { + + // listeners + + public void addConfigurationChangeListener(IMavenConfigurationChangeListener listener); + + // + + public boolean isOffline(); + + public String getGlobalSettingsFile(); + + //settable for embedded maven + public void setGlobalSettingsFile(String absolutePath); + + public String getUserSettingsFile(); + + public void setUserSettingsFile(String absolutePath); + + // resolution + + public boolean isDownloadSources(); + + public boolean isDownloadJavaDoc(); + + // problem reporting + + public String getJiraUsername(); + + public String getJiraPassword(); + + // maven execution + + public boolean isDebugOutput(); + + // + + public boolean isUpdateProjectsOnStartup(); + + public boolean isUpdateIndexesOnStartup(); + + // new experimental preferences + + public boolean isHideFoldersOfNestedProjects(); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfigurationChangeListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfigurationChangeListener.java new file mode 100644 index 00000000..c23eeb22 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenConfigurationChangeListener.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.core.runtime.CoreException; + +/** + * IMavenConfigurationChangeListener + * + * @author igor + */ +public interface IMavenConfigurationChangeListener { + + public void mavenConfigutationChange(MavenConfigurationChangeEvent event) throws CoreException; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenLauncherConfiguration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenLauncherConfiguration.java new file mode 100644 index 00000000..7b6526ec --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/IMavenLauncherConfiguration.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; + +/** + * Receive notification of content of plexus configuration. + * + * @author Igor Fedorenko + * + * @see MavenRuntime#createLauncherConfiguration + */ +public interface IMavenLauncherConfiguration { + + /** + * Special realm name used for launcher classpath entries. + */ + public static final String LAUNCHER_REALM = "]laucnher"; //$NON-NLS-1$ + + public void setMainType(String type, String realm); + + public void addRealm(String realm); + + public void addProjectEntry(IMavenProjectFacade facade); + + public void addArchiveEntry(String entry) throws CoreException; +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ISettingsChangeListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ISettingsChangeListener.java new file mode 100644 index 00000000..1fb0af01 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ISettingsChangeListener.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.core.runtime.CoreException; + +import org.apache.maven.settings.Settings; + +/** + * ISettingsChangeListener + * + * @author igor + */ +public interface ISettingsChangeListener { + + public void settingsChanged(Settings settings) throws CoreException; +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenConfigurationChangeEvent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenConfigurationChangeEvent.java new file mode 100644 index 00000000..323a53a5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenConfigurationChangeEvent.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + +/** + * MavenConfigurationChangeEvent + * + * @author igor + */ +public class MavenConfigurationChangeEvent implements MavenPreferenceConstants { + + public static final String P_USER_SETTINGS_FILE = MavenPreferenceConstants.P_USER_SETTINGS_FILE; + + private final String key; + private final Object newValue; + private final Object oldValue; + + public MavenConfigurationChangeEvent(String key, Object newValue, Object oldValue) { + this.key = key; + // TODO Auto-generated constructor stub + this.newValue = newValue; + this.oldValue = oldValue; + } + + public String getKey() { + return key; + } + + public Object getNewValue() { + return newValue; + } + + public Object getOldValue() { + return oldValue; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenModelManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenModelManager.java new file mode 100644 index 00000000..100d0716 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenModelManager.java @@ -0,0 +1,581 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.osgi.util.NLS; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.project.MavenProject; + +import org.sonatype.aether.artifact.ArtifactTypeRegistry; +import org.sonatype.aether.collection.CollectRequest; +import org.sonatype.aether.collection.DependencyCollectionException; +import org.sonatype.aether.collection.DependencyGraphTransformer; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.util.DefaultRepositorySystemSession; +import org.sonatype.aether.util.filter.ScopeDependencyFilter; +import org.sonatype.aether.util.graph.CloningDependencyVisitor; +import org.sonatype.aether.util.graph.FilteringDependencyVisitor; +import org.sonatype.aether.util.graph.transformer.ChainedDependencyGraphTransformer; +import org.sonatype.aether.util.graph.transformer.JavaEffectiveScopeCalculator; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.model.edit.pom.Build; +import org.eclipse.m2e.model.edit.pom.Configuration; +import org.eclipse.m2e.model.edit.pom.Dependency; +import org.eclipse.m2e.model.edit.pom.Exclusion; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.model.edit.pom.Plugin; +import org.eclipse.m2e.model.edit.pom.PomFactory; +import org.eclipse.m2e.model.edit.pom.util.PomResourceFactoryImpl; +import org.eclipse.m2e.model.edit.pom.util.PomResourceImpl; + + +/** + * Model manager used to read and and modify Maven models + * + * @author Eugene Kuleshov + * + * XXX fix circular dependency + */ +public class MavenModelManager { + + static final PomFactory POM_FACTORY = PomFactory.eINSTANCE; + + private final MavenProjectManager projectManager; + + private final MavenConsole console; + + private final IMaven maven; + + public MavenModelManager(IMaven maven, MavenProjectManager projectManager, MavenConsole console) { + this.maven = maven; + this.projectManager = projectManager; + this.console = console; + } + + public PomResourceImpl loadResource(IFile pomFile) throws CoreException { + String path = pomFile.getFullPath().toOSString(); + URI uri = URI.createPlatformResourceURI(path, true); + + try { + Resource resource = new PomResourceFactoryImpl().createResource(uri); + resource.load(new HashMap()); + return (PomResourceImpl)resource; + + } catch(Exception ex) { + String msg = NLS.bind(Messages.MavenModelManager_error_cannot_load, pomFile); + MavenLogger.log(msg, ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, ex)); + } + } + + public org.apache.maven.model.Model readMavenModel(InputStream reader) throws CoreException { + return maven.readModel(reader); + } + + public org.apache.maven.model.Model readMavenModel(File pomFile) throws CoreException { + return maven.readModel(pomFile); + } + + public org.apache.maven.model.Model readMavenModel(IFile pomFile) throws CoreException { + return maven.readModel(pomFile.getLocation().toFile()); + } + + public void createMavenModel(IFile pomFile, org.apache.maven.model.Model model) throws CoreException { + String pomFileName = pomFile.getLocation().toString(); + if(pomFile.exists()) { + String msg = NLS.bind(Messages.MavenModelManager_error_pom_exists, pomFileName); + console.logError(msg); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, null)); + } + + try { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + maven.writeModel(model, buf); + + // XXX MNGECLIPSE-495 + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(false); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document document = documentBuilder.parse(new ByteArrayInputStream(buf.toByteArray())); + Element documentElement = document.getDocumentElement(); + + NamedNodeMap attributes = documentElement.getAttributes(); + + if(attributes == null || attributes.getNamedItem("xmlns") == null) { //$NON-NLS-1$ + Attr attr = document.createAttribute("xmlns"); //$NON-NLS-1$ + attr.setTextContent("http://maven.apache.org/POM/4.0.0"); //$NON-NLS-1$ + documentElement.setAttributeNode(attr); + } + + if(attributes == null || attributes.getNamedItem("xmlns:xsi") == null) { //$NON-NLS-1$ + Attr attr = document.createAttribute("xmlns:xsi"); //$NON-NLS-1$ + attr.setTextContent("http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ + documentElement.setAttributeNode(attr); + } + + if(attributes == null || attributes.getNamedItem("xsi:schemaLocation") == null) { //$NON-NLS-1$ + Attr attr = document.createAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation"); //$NON-NLS-1$ //$NON-NLS-2$ + attr.setTextContent("http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"); //$NON-NLS-1$ + documentElement.setAttributeNode(attr); + } + + TransformerFactory transfac = TransformerFactory.newInstance(); + Transformer trans = transfac.newTransformer(); + trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //$NON-NLS-1$ + + buf.reset(); + trans.transform(new DOMSource(document), new StreamResult(buf)); + + pomFile.create(new ByteArrayInputStream(buf.toByteArray()), true, new NullProgressMonitor()); + + } catch(RuntimeException ex) { + String msg = NLS.bind(Messages.MavenModelManager_error_create, pomFileName, ex.toString()); + console.logError(msg); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, ex)); + } catch(Exception ex) { + String msg = NLS.bind(Messages.MavenModelManager_error_create, pomFileName, ex.toString()); + console.logError(msg); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, ex)); + } + } + + public synchronized DependencyNode readDependencyTree(IFile file, String classpath, + IProgressMonitor monitor) throws CoreException { + monitor.setTaskName(Messages.MavenModelManager_monitor_reading); + MavenProject mavenProject = readMavenProject(file, monitor); + + return readDependencyTree(mavenProject, classpath, monitor); + } + + public synchronized DependencyNode readDependencyTree(MavenProject mavenProject, + String classpath, IProgressMonitor monitor) throws CoreException { + monitor.setTaskName(Messages.MavenModelManager_monitor_building); + + IMaven maven = MavenPlugin.getDefault().getMaven(); + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(maven.createSession( + maven.createExecutionRequest(monitor), mavenProject).getRepositorySession()); + + DependencyGraphTransformer transformer = new ChainedDependencyGraphTransformer(new JavaEffectiveScopeCalculator(), + new NearestVersionConflictResolver()); + session.setDependencyGraphTransformer(transformer); + + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(maven.getProjectRealm(mavenProject)); + + ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry(); + + CollectRequest request = new CollectRequest(); + request.setRequestContext("project"); //$NON-NLS-1$ + request.setRepositories(mavenProject.getRemoteProjectRepositories()); + + for(org.apache.maven.model.Dependency dependency : mavenProject.getDependencies()) { + request.addDependency(RepositoryUtils.toDependency(dependency, stereotypes)); + } + + DependencyManagement depMngt = mavenProject.getDependencyManagement(); + if(depMngt != null) { + for(org.apache.maven.model.Dependency dependency : depMngt.getDependencies()) { + request.addManagedDependency(RepositoryUtils.toDependency(dependency, stereotypes)); + } + } + + DependencyNode node; + try { + node = MavenPlugin.getDefault().getRepositorySystem().collectDependencies(session, request).getRoot(); + } catch(DependencyCollectionException ex) { + String msg = Messages.MavenModelManager_error_read; + MavenLogger.log(msg, ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, ex)); + } + + Collection<String> scopes = new HashSet<String>(); + Collections.addAll(scopes, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_COMPILE, Artifact.SCOPE_PROVIDED, + Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST); + if(Artifact.SCOPE_COMPILE.equals(classpath)) { + scopes.remove(Artifact.SCOPE_COMPILE); + scopes.remove(Artifact.SCOPE_SYSTEM); + scopes.remove(Artifact.SCOPE_PROVIDED); + } else if(Artifact.SCOPE_RUNTIME.equals(classpath)) { + scopes.remove(Artifact.SCOPE_COMPILE); + scopes.remove(Artifact.SCOPE_RUNTIME); + } else if(Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals(classpath)) { + scopes.remove(Artifact.SCOPE_COMPILE); + scopes.remove(Artifact.SCOPE_SYSTEM); + scopes.remove(Artifact.SCOPE_PROVIDED); + scopes.remove(Artifact.SCOPE_RUNTIME); + } else { + scopes.clear(); + } + + CloningDependencyVisitor cloner = new CloningDependencyVisitor(); + node.accept(new FilteringDependencyVisitor(cloner, new ScopeDependencyFilter(null, scopes))); + node = cloner.getRootNode(); + + return node; + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + public MavenProject readMavenProject(IFile file, IProgressMonitor monitor) throws CoreException { + IMavenProjectFacade projectFacade = projectManager.create(file, true, monitor); + MavenProject mavenProject = projectFacade.getMavenProject(monitor); + return mavenProject; + } + +// public ProjectDocument readProjectDocument(IFile pomFile) throws CoreException { +// String name = pomFile.getProject().getName() + "/" + pomFile.getProjectRelativePath(); +// try { +// return ProjectDocument.Factory.parse(pomFile.getLocation().toFile(), getXmlOptions()); +// } catch(XmlException ex) { +// String msg = "Unable to parse " + name; +// console.logError(msg + "; " + ex.toString()); +// throw new CoreException(new Status(IStatus.ERROR, MavenPlugin.PLUGIN_ID, -1, msg, ex)); +// } catch(IOException ex) { +// String msg = "Unable to read " + name; +// console.logError(msg + "; " + ex.toString()); +// throw new CoreException(new Status(IStatus.ERROR, MavenPlugin.PLUGIN_ID, -1, msg, ex)); +// } +// } +// +// public ProjectDocument readProjectDocument(File pom) throws CoreException { +// try { +// return ProjectDocument.Factory.parse(pom, getXmlOptions()); +// } catch(XmlException ex) { +// String msg = "Unable to parse " + pom.getAbsolutePath(); +// console.logError(msg + "; " + ex.toString()); +// throw new CoreException(new Status(IStatus.ERROR, MavenPlugin.PLUGIN_ID, -1, msg, ex)); +// } catch(IOException ex) { +// String msg = "Unable to read " + pom.getAbsolutePath(); +// console.logError(msg + "; " + ex.toString()); +// throw new CoreException(new Status(IStatus.ERROR, MavenPlugin.PLUGIN_ID, -1, msg, ex)); +// } +// } + + public void updateProject(IFile pomFile, ProjectUpdater updater) { + File pom = pomFile.getLocation().toFile(); + PomResourceImpl resource = null; + try { + resource = loadResource(pomFile); + updater.update(resource.getModel()); + resource.save(Collections.EMPTY_MAP); + } catch(Exception ex) { + String msg = "Unable to update " + pom; + console.logError(msg + "; " + ex.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + } finally { + if (resource != null) { + resource.unload(); + } + } + } + + public void addDependency(IFile pomFile, org.apache.maven.model.Dependency dependency) { + updateProject(pomFile, new DependencyAdder(dependency)); + } + + public void addModule(IFile pomFile, final String moduleName) { + updateProject(pomFile, new ModuleAdder(moduleName)); + } + +// /** +// * Project updater for adding Maven namespace declaration +// */ +// public static class NamespaceAdder extends ProjectUpdater { +// +// public void update(Model model) { +// DocumentRoot documentRoot = PomFactory.eINSTANCE.createDocumentRoot(); +// EMap<String, String> prefixMap = documentRoot.getXMLNSPrefixMap(); +// EMap<String, String> schemaLocation = documentRoot.getXSISchemaLocation(); +// +// // xmlns="http://maven.apache.org/POM/4.0.0" +// // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +// // xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" +// +//// XmlCursor cursor = project.newCursor(); +//// cursor.toNextToken(); +//// if(!cursor.toFirstAttribute()) { +//// cursor.toNextToken(); +//// } +//// +//// String uri = ProjectDocument.type.getDocumentElementName().getNamespaceURI(); +//// cursor.insertNamespace("", uri); +//// cursor.insertNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); +//// cursor.insertAttributeWithValue( // +//// new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"), uri +//// + " http://maven.apache.org/xsd/maven-4.0.0.xsd"); +// } +// +// } + + /** + * Project updater for adding dependencies + */ + public static class DependencyAdder extends ProjectUpdater { + + private final org.apache.maven.model.Dependency dependency; + + public DependencyAdder(org.apache.maven.model.Dependency dependency) { + this.dependency = dependency; + } + + public void update(org.eclipse.m2e.model.edit.pom.Model model) { + Dependency dependency = POM_FACTORY.createDependency(); + + dependency.setGroupId(this.dependency.getGroupId()); + dependency.setArtifactId(this.dependency.getArtifactId()); + + if(this.dependency.getVersion()!=null) { + dependency.setVersion(this.dependency.getVersion()); + } + + if(this.dependency.getClassifier() != null) { + dependency.setClassifier(this.dependency.getClassifier()); + } + + if(this.dependency.getType() != null // + && !"jar".equals(this.dependency.getType()) // //$NON-NLS-1$ + && !"null".equals(this.dependency.getType())) { // guard against MNGECLIPSE-622 //$NON-NLS-1$ + dependency.setType(this.dependency.getType()); + } + + if(this.dependency.getScope() != null && !"compile".equals(this.dependency.getScope())) { //$NON-NLS-1$ + dependency.setScope(this.dependency.getScope()); + } + + if(this.dependency.getSystemPath() != null) { + dependency.setSystemPath(this.dependency.getSystemPath()); + } + + if(this.dependency.isOptional()) { + dependency.setOptional("true"); //$NON-NLS-1$ + } + + if(!this.dependency.getExclusions().isEmpty()) { + + Iterator<org.apache.maven.model.Exclusion> it = this.dependency.getExclusions().iterator(); + while(it.hasNext()) { + org.apache.maven.model.Exclusion e = it.next(); + Exclusion exclusion = POM_FACTORY.createExclusion(); + exclusion.setGroupId(e.getGroupId()); + exclusion.setArtifactId(e.getArtifactId()); + dependency.getExclusions().add(exclusion); + } + } + + // search for dependency with same GAC and remove if found + Iterator<Dependency> it = model.getDependencies().iterator(); + boolean mergeScope = false; + String oldScope = Artifact.SCOPE_COMPILE; + while (it.hasNext()) { + Dependency dep = it.next(); + if (dep.getGroupId().equals(dependency.getGroupId()) && + dep.getArtifactId().equals(dependency.getArtifactId()) && + compareNulls(dep.getClassifier(), dependency.getClassifier())) { + oldScope = dep.getScope(); + it.remove(); + mergeScope = true; + } + } + + if (mergeScope) { + // merge scopes + if (oldScope == null) { + oldScope = Artifact.SCOPE_COMPILE; + } + + String newScope = this.dependency.getScope(); + if (newScope == null) { + newScope = Artifact.SCOPE_COMPILE; + } + + if (!oldScope.equals(newScope)) { + boolean systemScope = false; + boolean providedScope = false; + boolean compileScope = false; + boolean runtimeScope = false; + boolean testScope = false; + + // test old scope + if ( Artifact.SCOPE_COMPILE.equals( oldScope ) ) { + systemScope = true; + providedScope = true; + compileScope = true; + runtimeScope = false; + testScope = false; + } else if ( Artifact.SCOPE_RUNTIME.equals( oldScope ) ) { + systemScope = false; + providedScope = false; + compileScope = true; + runtimeScope = true; + testScope = false; + } else if ( Artifact.SCOPE_TEST.equals( oldScope ) ) { + systemScope = true; + providedScope = true; + compileScope = true; + runtimeScope = true; + testScope = true; + } + + // merge with new one + if ( Artifact.SCOPE_COMPILE.equals( newScope ) ) { + systemScope = systemScope || true; + providedScope = providedScope || true; + compileScope = compileScope || true; + runtimeScope = runtimeScope || false; + testScope = testScope || false; + } else if ( Artifact.SCOPE_RUNTIME.equals( newScope ) ) { + systemScope = systemScope || false; + providedScope = providedScope || false; + compileScope = compileScope || true; + runtimeScope = runtimeScope || true; + testScope = testScope || false; + } else if ( Artifact.SCOPE_TEST.equals( newScope ) ) { + systemScope = systemScope || true; + providedScope = providedScope || true; + compileScope = compileScope || true; + runtimeScope = runtimeScope || true; + testScope = testScope || true; + } + + if (testScope) { + newScope = Artifact.SCOPE_TEST; + } else if (runtimeScope) { + newScope = Artifact.SCOPE_RUNTIME; + } else if (compileScope) { + newScope = Artifact.SCOPE_COMPILE; + } else { + // unchanged + } + + dependency.setScope(newScope); + } + } + + model.getDependencies().add(dependency); + } + + @SuppressWarnings("null") + private boolean compareNulls(String s1, String s2) { + if (s1 == null && s2 == null) { + return true; + } + if ((s1 == null && s2 != null) || (s2 == null && s1 != null)) { + return false; + } + return s1.equals(s2); + } + } + + + /** + * Project updater for adding modules + */ + public static class ModuleAdder extends ProjectUpdater { + + private final String moduleName; + + public ModuleAdder(String moduleName) { + this.moduleName = moduleName; + } + + public void update(Model model) { + model.getModules().add(moduleName); + } + } + + /** + * Project updater for adding plugins + */ + public static class PluginAdder extends ProjectUpdater { + + private final String groupId; + private final String artifactId; + private final String version; + + public PluginAdder(String groupId, String artifactId, String version) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + } + + public void update(Model model) { + Build build = model.getBuild(); + if(build==null) { + build = POM_FACTORY.createBuild(); + model.setBuild(build); + } + + Plugin plugin = POM_FACTORY.createPlugin(); + + if(!"org.apache.maven.plugins".equals(this.groupId)) { //$NON-NLS-1$ + plugin.setGroupId(this.groupId); + } + + plugin.setArtifactId(this.artifactId); + + if(this.version != null) { + plugin.setVersion(this.version); + } + + Configuration configuration = POM_FACTORY.createConfiguration(); + plugin.setConfiguration(configuration); + + build.getPlugins().add(plugin); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntime.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntime.java new file mode 100644 index 00000000..31940164 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntime.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * Maven runtime + * + * @author Eugene Kuleshov + * @author Igor Fedorenko + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface MavenRuntime { + + public abstract boolean isEditable(); + + /** + * Reads m2.conf file and notifies configuration collector of the logical content of plexus configuration. + * + * Collector callback methods are invoked in the order corresponding + * configuration elements are present in m2.conf file. + */ + public abstract void createLauncherConfiguration(IMavenLauncherConfiguration collector, IProgressMonitor monitor) throws CoreException; + + public abstract String getLocation(); + + public abstract String getSettings(); + + public abstract boolean isAvailable(); + + public String getVersion(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntimeManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntimeManager.java new file mode 100644 index 00000000..0fd11501 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/MavenRuntimeManager.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.m2e.core.internal.embedder.MavenEmbeddedRuntime; +import org.eclipse.m2e.core.internal.embedder.MavenExternalRuntime; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + +/** + * Maven runtime manager + * + * @author Eugene Kuleshov + */ +public class MavenRuntimeManager { + + public static final String DEFAULT = "DEFAULT"; //$NON-NLS-1$ + + public static final String EMBEDDED = "EMBEDDED"; //$NON-NLS-1$ + + public static final String WORKSPACE = "WORKSPACE"; //$NON-NLS-1$ + + private final IPreferenceStore preferenceStore; + + private Map<String, MavenRuntime> runtimes = new LinkedHashMap<String, MavenRuntime>(); + + private MavenRuntime embeddedRuntime; + + private MavenRuntime workspaceRuntime; + + private MavenRuntime defaultRuntime; + + + public MavenRuntimeManager(IPreferenceStore preferenceStore) { + this.preferenceStore = preferenceStore; + initRuntimes(); + } + + public void setEmbeddedRuntime(MavenRuntime embeddedRuntime) { + this.embeddedRuntime = embeddedRuntime; + } + + public void setWorkspaceRuntime(MavenRuntime workspaceRuntime) { + this.workspaceRuntime = workspaceRuntime; + } + + public MavenRuntime getDefaultRuntime() { + if(defaultRuntime == null || !defaultRuntime.isAvailable()) { + return embeddedRuntime; + } + return this.defaultRuntime; + } + + public MavenRuntime getRuntime(String location) { + if(location==null || location.length()==0 || DEFAULT.equals(location)) { + return getDefaultRuntime(); + } + if(EMBEDDED.equals(location)) { + return embeddedRuntime; + } + if(WORKSPACE.equals(location)) { + return workspaceRuntime; + } + return runtimes.get(location); + } + + public List<MavenRuntime> getMavenRuntimes() { + ArrayList<MavenRuntime> runtimes = new ArrayList<MavenRuntime>(); + + runtimes.add(embeddedRuntime); + + if(workspaceRuntime != null && workspaceRuntime.isAvailable()) { + runtimes.add(workspaceRuntime); + } + + for(MavenRuntime runtime : this.runtimes.values()) { + if(runtime.isAvailable()) { + runtimes.add(runtime); + } + } + return runtimes; + } + + public void reset() { + preferenceStore.setToDefault(MavenPreferenceConstants.P_RUNTIMES); + preferenceStore.setToDefault(MavenPreferenceConstants.P_DEFAULT_RUNTIME); + + initRuntimes(); + } + + public void setDefaultRuntime(MavenRuntime runtime) { + this.defaultRuntime = runtime; + + if(runtime == null) { + preferenceStore.setToDefault(MavenPreferenceConstants.P_DEFAULT_RUNTIME); + } else { + preferenceStore.setValue(MavenPreferenceConstants.P_DEFAULT_RUNTIME, runtime.getLocation()); + } + } + + public void setRuntimes(List<MavenRuntime> runtimes) { + this.runtimes.clear(); + + String separator = ""; //$NON-NLS-1$ + StringBuffer sb = new StringBuffer(); + for(MavenRuntime runtime : runtimes) { + if(runtime.isEditable()) { + this.runtimes.put(runtime.getLocation(), runtime); + sb.append(separator).append(runtime.getLocation()); + separator = "|"; //$NON-NLS-1$ + } + } + preferenceStore.setValue(MavenPreferenceConstants.P_RUNTIMES, sb.toString()); + } + + private void initRuntimes() { + runtimes.clear(); + + defaultRuntime = null; + + String selected = preferenceStore.getString(MavenPreferenceConstants.P_DEFAULT_RUNTIME); + + String runtimesPreference = preferenceStore.getString(MavenPreferenceConstants.P_RUNTIMES); + if(runtimesPreference!=null && runtimesPreference.length()>0) { + String[] locations = runtimesPreference.split("\\|"); //$NON-NLS-1$ + for(int i = 0; i < locations.length; i++ ) { + MavenRuntime runtime = createExternalRuntime(locations[i]); + runtimes.put(runtime.getLocation(), runtime); + if(runtime.getLocation().equals(selected)) { + defaultRuntime = runtime; + } + } + } + } + + public static MavenRuntime createExternalRuntime(String location) { + return new MavenExternalRuntime(location); + } + + public String getGlobalSettingsFile() { + //only return the preference store value for the global settings file if its an embedded runtime + if(defaultRuntime == null || defaultRuntime instanceof MavenEmbeddedRuntime){ + String globalSettings = preferenceStore.getString(MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE); + return globalSettings.trim().length()==0 ? null : globalSettings; + } + return defaultRuntime == null ? null : defaultRuntime.getSettings(); + } + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/NearestVersionConflictResolver.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/NearestVersionConflictResolver.java new file mode 100644 index 00000000..4e24efe3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/NearestVersionConflictResolver.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import org.sonatype.aether.RepositoryException; +import org.sonatype.aether.collection.DependencyGraphTransformationContext; +import org.sonatype.aether.collection.DependencyGraphTransformer; +import org.sonatype.aether.collection.UnsolvableVersionConflictException; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.util.graph.DefaultDependencyNode; +import org.sonatype.aether.util.graph.transformer.ConflictIdSorter; +import org.sonatype.aether.util.graph.transformer.TransformationContextKeys; +import org.sonatype.aether.version.Version; +import org.sonatype.aether.version.VersionConstraint; + + +class NearestVersionConflictResolver implements DependencyGraphTransformer { + + public DependencyNode transformGraph(DependencyNode node, DependencyGraphTransformationContext context) + throws RepositoryException { + List<?> sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS); + if(sortedConflictIds == null) { + ConflictIdSorter sorter = new ConflictIdSorter(); + sorter.transformGraph(node, context); + + sortedConflictIds = (List<?>) context.get(TransformationContextKeys.SORTED_CONFLICT_IDS); + } + + Map<?, ?> conflictIds = (Map<?, ?>) context.get(TransformationContextKeys.CONFLICT_IDS); + if(conflictIds == null) { + throw new RepositoryException("conflict groups have not been identified"); + } + + Map<DependencyNode, Integer> depths = new IdentityHashMap<DependencyNode, Integer>(conflictIds.size()); + for(Object key : sortedConflictIds) { + ConflictGroup group = new ConflictGroup(key); + depths.clear(); + selectVersion(node, null, 0, depths, group, conflictIds); + pruneNonSelectedVersions(group, conflictIds); + } + + return node; + } + + private void selectVersion(DependencyNode node, DependencyNode parent, int depth, + Map<DependencyNode, Integer> depths, ConflictGroup group, Map<?, ?> conflictIds) throws RepositoryException { + Integer smallestDepth = depths.get(node); + if(smallestDepth == null || smallestDepth.intValue() > depth) { + depths.put(node, Integer.valueOf(depth)); + } else { + return; + } + + Object key = conflictIds.get(node); + if(group.key.equals(key)) { + Position pos = new Position(parent, depth); + if(parent != null) { + group.positions.add(pos); + } + + if(!group.isAcceptable(node.getVersion())) { + return; + } + + group.candidates.put(node, pos); + + if(!node.getVersionConstraint().getRanges().isEmpty()) { + group.constraints.add(node.getVersionConstraint()); + } + + if(group.version == null || isNearer(pos, node.getVersion(), group.position, group.version)) { + group.winner = node; + group.version = node.getVersion(); + group.position = pos; + } + + if(!group.isAcceptable(group.version)) { + group.winner = null; + group.version = null; + + for(Iterator<Map.Entry<DependencyNode, Position>> it = group.candidates.entrySet().iterator(); it.hasNext();) { + Map.Entry<DependencyNode, Position> entry = it.next(); + Version version = entry.getKey().getVersion(); + pos = entry.getValue(); + + if(!group.isAcceptable(version)) { + it.remove(); + } else if(group.version == null || isNearer(pos, version, group.position, group.version)) { + group.winner = entry.getKey(); + group.version = version; + group.position = pos; + } + } + + if(group.version == null) { + Collection<String> versions = new LinkedHashSet<String>(); + for(VersionConstraint constraint : group.constraints) { + versions.add(constraint.toString()); + } + throw new UnsolvableVersionConflictException(group.key, versions); + } + } + } + + depth++ ; + + for(DependencyNode child : node.getChildren()) { + selectVersion(child, node, depth, depths, group, conflictIds); + } + } + + private boolean isNearer(Position pos1, Version ver1, Position pos2, Version ver2) { + if(pos1.depth < pos2.depth) { + return true; + } else if(pos1.depth == pos2.depth && pos1.parent == pos2.parent && ver1.compareTo(ver2) > 0) { + return true; + } + return false; + } + + private void pruneNonSelectedVersions(ConflictGroup group, Map<?, ?> conflictIds) { + for(Position pos : group.positions) { + ConflictNode conflictNode = null; + + for(ListIterator<DependencyNode> it = pos.parent.getChildren().listIterator(); it.hasNext();) { + DependencyNode child = it.next(); + + Object key = conflictIds.get(child); + + if(group.key.equals(key)) { + if(!group.pruned && group.position.depth == pos.depth && group.version.equals(child.getVersion())) { + group.pruned = true; + } else if(pos.equals(group.position)) { + it.remove(); + } else if(conflictNode == null) { + conflictNode = new ConflictNode(child, group.winner); + it.set(conflictNode); + } else { + it.remove(); + + if(conflictNode.getVersion().compareTo(child.getVersion()) < 0) { + conflictNode.setDependency(child.getDependency()); + conflictNode.setVersion(child.getVersion()); + conflictNode.setVersionConstraint(child.getVersionConstraint()); + conflictNode.setPremanagedVersion(child.getPremanagedVersion()); + conflictNode.setRelocations(child.getRelocations()); + } + } + } + } + } + } + + static final class ConflictGroup { + + final Object key; + + final Collection<VersionConstraint> constraints = new HashSet<VersionConstraint>(); + + final Map<DependencyNode, Position> candidates = new IdentityHashMap<DependencyNode, Position>(32); + + DependencyNode winner; + + Version version; + + Position position; + + final Collection<Position> positions = new LinkedHashSet<Position>(); + + boolean pruned; + + public ConflictGroup(Object key) { + this.key = key; + this.position = new Position(null, Integer.MAX_VALUE); + } + + boolean isAcceptable(Version version) { + for(VersionConstraint constraint : constraints) { + if(!constraint.containsVersion(version)) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return key + " > " + version; //$NON-NLS-1$ + } + + } + + static final class Position { + + final DependencyNode parent; + + final int depth; + + final int hash; + + public Position(DependencyNode parent, int depth) { + this.parent = parent; + this.depth = depth; + hash = 31 * System.identityHashCode(parent) + depth; + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } else if(!(obj instanceof Position)) { + return false; + } + Position that = (Position) obj; + return this.parent == that.parent && this.depth == that.depth; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return depth + " > " + parent; //$NON-NLS-1$ + } + + } + + static final class ConflictNode extends DefaultDependencyNode { + + public ConflictNode(DependencyNode node, DependencyNode related) { + super(node); + setAliases(Collections.singletonList(related.getDependency().getArtifact())); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ProjectUpdater.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ProjectUpdater.java new file mode 100644 index 00000000..42fbf301 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/embedder/ProjectUpdater.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.embedder; + +import org.eclipse.m2e.model.edit.pom.Model; + +/** + * Project updater + * + * @author Eugene Kuleshov + */ +public abstract class ProjectUpdater { + + public abstract void update(Model model); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IIndex.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IIndex.java new file mode 100644 index 00000000..0add0fd5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IIndex.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import java.io.File; +import java.util.Collection; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * @author igor + */ +public interface IIndex { + + // search keys + + public static final String SEARCH_GROUP = "groupId"; //$NON-NLS-1$ + + public static final String SEARCH_ARTIFACT = "artifact"; //$NON-NLS-1$ + + public static final String SEARCH_PLUGIN = "plugin"; //$NON-NLS-1$ + + public static final String SEARCH_ARCHETYPE = "archetype"; //$NON-NLS-1$ + + public static final String SEARCH_PACKAGING = "packaging"; //$NON-NLS-1$ + + public static final String SEARCH_SHA1 = "sha1"; //$NON-NLS-1$ + + /** + * like SEARCH_ARTIFACT but will only return artifacts with packaging == pom + */ + public static final String SEARCH_PARENTS = "parents"; //$NON-NLS-1$ + + // search classifiers + +// public enum SearchClassifiers { +// JARS, +// +// JAVADOCS, +// +// SOURCES, +// +// TESTS +// } +// +// public Set<SearchClassifiers> ALL_CLASSIFIERS = new HashSet<IIndex.SearchClassifiers>(Arrays.asList(SearchClassifiers +// .values())); + + // + + public static final int SEARCH_JARS = 1 << 0; + + public static final int SEARCH_JAVADOCS = 1 << 1; + + public static final int SEARCH_SOURCES = 1 << 2; + + public static final int SEARCH_TESTS = 1 << 3; + + public static final int SEARCH_ALL = 15; + + // availability flags + + public static final int PRESENT = 1; + + public static final int NOT_PRESENT = 0; + + public static final int NOT_AVAILABLE = 2; + + // index queries + + public IndexedArtifactFile getIndexedArtifactFile(ArtifactKey artifact) throws CoreException; + + public IndexedArtifactFile identify(File file) throws CoreException; + + /** + * Performs a search for artifacts with given parameters. + * + * @param groupId + * @param artifactId + * @param version + * @param packaging + * @return + * @throws CoreException + */ + public Collection<IndexedArtifact> find(SearchExpression groupId, SearchExpression artifactId, + SearchExpression version, SearchExpression packaging) throws CoreException; + + /** + * Performs a search for artifacts with given parameters. Similar to + * {@link IIndex#find(SearchExpression, SearchExpression, SearchExpression, SearchExpression)}, but here you are able + * to pass in multiple values for all searches. All elements of collections will form an "OR" of one query. + * + * @param groupId + * @param artifactId + * @param version + * @param packaging + * @return + * @throws CoreException + */ + public Collection<IndexedArtifact> find(Collection<SearchExpression> groupId, + Collection<SearchExpression> artifactId, Collection<SearchExpression> version, + Collection<SearchExpression> packaging) throws CoreException; + + /** + * Convenience method to search in all indexes enabled for repositories defined in settings.xml. This method always + * performs "scored" search. + */ + public Map<String, IndexedArtifact> search(SearchExpression expression, String searchType) throws CoreException; + + /** + * Convenience method to search in all indexes enabled for repositories defined in settings.xml. This method always + * performs "scored" search. + * + * @param term - search term + * @param searchType - query type. Should be one of the SEARCH_* values. + * @param classifier - the type of classifiers to search for, SEARCH_ALL, SEARCH_JAVADOCS, SEARCH_SOURCES, + * SEARCH_TESTS + */ + public Map<String, IndexedArtifact> search(SearchExpression expression, String searchType, int classifier) + throws CoreException; +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IMutableIndex.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IMutableIndex.java new file mode 100644 index 00000000..c6d11b3f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IMutableIndex.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import java.io.File; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * @author igor + */ +public interface IMutableIndex extends IIndex { + + // index content manipulation + + public void addArtifact(File pomFile, ArtifactKey artifactKey); + + public void removeArtifact(File pomFile, ArtifactKey artifactKey); + + // reindexing + + public void updateIndex(boolean force, IProgressMonitor monitor) throws CoreException; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexListener.java new file mode 100644 index 00000000..27ac3473 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexListener.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import org.eclipse.m2e.core.repository.IRepository; + + + +/** + * IndexListener + * + * @author Eugene Kuleshov + */ +public interface IndexListener { + + public void indexAdded(IRepository repository); + + public void indexRemoved(IRepository repository); + + public void indexChanged(IRepository repository); + + public void indexUpdating(IRepository repository); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexManager.java new file mode 100644 index 00000000..0dbf701a --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexManager.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; + + +public interface IndexManager { + + // well-known indexes + + String LOCAL_INDEX = "local"; //$NON-NLS-1$ + + String WORKSPACE_INDEX = "workspace"; //$NON-NLS-1$ + + // + + IMutableIndex getWorkspaceIndex(); + + IMutableIndex getLocalIndex(); + + /** + * For Maven projects, returns index of all repositories configured for the project. Index includes repositories + * defined in the project pom.xml, inherited from parent projects and defined in enabled profiles in settings.xml. If + * project is null or is not a maven project, returns index that includes repositories defined in profiles enabled by + * default in settings.xml. + */ + IIndex getIndex(IProject project) throws CoreException; + + /** + * Returns index aggregating all indexes enabled for repositories defined in settings.xml + * + * @return + * @throws CoreException + */ + IIndex getAllIndexes() throws CoreException; + + // + + void removeIndexListener(IndexListener listener); + + void addIndexListener(IndexListener listener); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifact.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifact.java new file mode 100644 index 00000000..756bcfb8 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifact.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.maven.artifact.versioning.ArtifactVersion; + + +public class IndexedArtifact{ + + public static final Comparator<IndexedArtifactFile> FILE_INFO_COMPARATOR = new Comparator<IndexedArtifactFile>() { + + @SuppressWarnings("unchecked") + public int compare(IndexedArtifactFile f1, IndexedArtifactFile f2) { + ArtifactVersion v1 = f1.getArtifactVersion(); + ArtifactVersion v2 = f2.getArtifactVersion(); + int r = -v1.compareTo(v2); + if(r!=0) { + return r; + } + + String c1 = f1.classifier; + String c2 = f2.classifier; + if(c1 == null) { + return c2 == null ? 0 : -1; + } + if(c2 == null) { + return 1; + } + return c1.compareTo(c2); + } + + }; + + private final String group; + + private final String artifact; + + private final String packageName; + + private final String className; + + private final String packaging; + + //a non-zero odd-prime hash seed + private static final int SEED = 17; + + /** + * Set<IndexedArtifactFile> + */ + private final Set<IndexedArtifactFile> files = new TreeSet<IndexedArtifactFile>(FILE_INFO_COMPARATOR); + + public IndexedArtifact(String group, String artifact, String packageName, String className, String packaging) { + this.group = group; + this.artifact = artifact; + this.packageName = packageName; + this.className = className; + this.packaging = packaging; + } + + public void addFile(IndexedArtifactFile indexedArtifactFile) { + getFiles().add(indexedArtifactFile); + } + + public String getPackageName(){ + if(packageName != null && packageName.startsWith(".") && packageName.length()>1){ //$NON-NLS-1$ + return packageName.substring(1); + } + return packageName; + } + + public String toString() { + StringBuffer sb = new StringBuffer("\n" + getClassname() + " " + packageName + " " + getGroupId() + " : " + getArtifactId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + return sb.toString(); + } + + public String getGroupId() { + return group; + } + + public String getArtifactId() { + return artifact; + } + + public String getPackaging() { + return packaging; + } + + public String getClassname() { + return className; + } + + public Set<IndexedArtifactFile> getFiles() { + return files; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int result = SEED; + result *= fieldHash(getGroupId()); + result *= fieldHash(getArtifactId()); + result *= fieldHash(getPackaging()); + result *= fieldHash(getClassname()); + result *= fieldHash(getPackageName()); + return result; + } + + private int fieldHash(Object field){ + if(field == null){ + return SEED; + } + return field.hashCode(); + } + + /** + * Assumes all the fields are important for equals. + */ + public boolean equals(Object artifact){ + if(this == artifact){ + return true; + } else if(!(artifact instanceof IndexedArtifact)){ + return false; + } else { + IndexedArtifact other = (IndexedArtifact)artifact; + return fieldsEqual(this.getGroupId(), other.getGroupId()) && + fieldsEqual(this.getArtifactId(), other.getArtifactId()) && + fieldsEqual(this.getPackageName(), other.getPackageName()) && + fieldsEqual(this.getPackaging(), other.getPackaging()) && + fieldsEqual(this.getClassname(), other.getClassname()); + } + } + + private boolean fieldsEqual(Object field1, Object field2){ + return field1 == null ? field2 == null : field1.equals(field2); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifactFile.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifactFile.java new file mode 100644 index 00000000..46c60100 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/IndexedArtifactFile.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +import java.util.Date; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.model.Dependency; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + +public class IndexedArtifactFile implements IAdaptable { + + public final String repository; + + public final String group; + + public final String artifact; + + public final String fname; + + public final String version; + + private ArtifactVersion artifactVersion; + + public final String type; + + public final String classifier; + + public final long size; + + public final Date date; + + public final int sourcesExists; + + public final int javadocExists; + + public final String prefix; + + public final List<String> goals; + + public IndexedArtifactFile(String repository, String group, String artifact, String version, String type, + String classifier, String fname, long size, Date date, int sourcesExists, int javadocExists, String prefix, + List<String> goals) { + this.repository = repository; + this.group = group; + this.artifact = artifact; + this.version = version; + this.type = type; + this.classifier = classifier; + this.fname = fname; + this.size = size; + this.date = date == null ? null : new Date(date.getTime()); + this.sourcesExists = sourcesExists; + this.javadocExists = javadocExists; + this.prefix = prefix; + this.goals = goals; + } + + public ArtifactVersion getArtifactVersion() { + if (artifactVersion == null) { + artifactVersion = new DefaultArtifactVersion(version); + } + return artifactVersion; + } + + public Dependency getDependency() { + Dependency dependency = new Dependency(); + dependency.setArtifactId(artifact); + dependency.setGroupId(group); + dependency.setVersion(version); + dependency.setClassifier(classifier); + dependency.setType(type); // TODO: investigate difference between packaging and type + //http://docs.codehaus.org/display/MAVEN/Packaging+vs+Type+-+Derived+and+Attached+Artifacts + return dependency; + } + + public ArtifactKey getArtifactKey() { + return new ArtifactKey(group, artifact, version, classifier); + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if(adapter==ArtifactKey.class) { + return getArtifactKey(); + } + return null; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTyped.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTyped.java new file mode 100644 index 00000000..e3c9363b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTyped.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * MatchTyped is a interface that describes the wanted match type to be used. + * + * @author cstamas + */ +public interface MatchTyped { + + public enum MatchType { + /** Exact match wanted */ + EXACT, + /** Partial match wanted, like prefix, contains, etc. */ + PARTIAL; + }; + + MatchType getMatchType(); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTypedStringSearchExpression.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTypedStringSearchExpression.java new file mode 100644 index 00000000..23050079 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/MatchTypedStringSearchExpression.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * MatchTypedStringSearchExpression + * + * @author cstamas + */ +public class MatchTypedStringSearchExpression extends StringSearchExpression implements MatchTyped { + + private final MatchType matchType; + + public MatchTypedStringSearchExpression(final String expression, final MatchType matchType) { + super(expression); + this.matchType = matchType; + } + + public MatchType getMatchType() { + return matchType; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SearchExpression.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SearchExpression.java new file mode 100644 index 00000000..8b4592b2 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SearchExpression.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * SearchExpression is a wrapper interface for expressions representable as plain strings to be used within searches. + * + * @author cstamas + */ +public interface SearchExpression { + + /** + * Returns the expression value as plain java String. + * + * @return + */ + String getStringValue(); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SourcedSearchExpression.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SourcedSearchExpression.java new file mode 100644 index 00000000..f9932d03 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/SourcedSearchExpression.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * SourcedSearchExpression is a search expression usually "sourced" from some programmatic source, and we already know + * it is complete, exact value that we want to search for. Indexer will try to match exactly the provided string value, + * no more no less. + * + * @author cstamas + */ +public class SourcedSearchExpression extends MatchTypedStringSearchExpression { + + public SourcedSearchExpression(String expression) { + super(expression, MatchType.EXACT); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/StringSearchExpression.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/StringSearchExpression.java new file mode 100644 index 00000000..901d0c37 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/StringSearchExpression.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * StringSearchExpression is a SearchExpression that has String value. + * + * @author cstamas + */ +public class StringSearchExpression implements SearchExpression { + + private final String expression; + + public StringSearchExpression(String expression) { + assert expression != null && expression.trim().length() > 0 : "The expression cannot be empty!"; + this.expression = expression; + } + + public String getStringValue() { + return expression; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/UserInputSearchExpression.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/UserInputSearchExpression.java new file mode 100644 index 00000000..dfa7ea7b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/index/UserInputSearchExpression.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.index; + +/** + * UserInputSearchExpression is a search expression usually coming from user input (like some UI dialogue, element or + * CLI). It will be normalized and tokenized and then a search will happen against it. Search expressions of this type + * will always provide "broader" results, since it defaults to prefix searches. + * + * @author cstamas + */ +public class UserInputSearchExpression extends MatchTypedStringSearchExpression { + + public UserInputSearchExpression(String expression) { + super(expression, MatchType.PARTIAL); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/ExtensionReader.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/ExtensionReader.java new file mode 100644 index 00000000..c0c542e6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/ExtensionReader.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.osgi.framework.Bundle; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IContributor; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; + + +/** + * Extension reader + * + * @author Eugene Kuleshov + */ +public class ExtensionReader { + + public static final String EXTENSION_ARCHETYPES = IMavenConstants.PLUGIN_ID + ".archetypeCatalogs"; //$NON-NLS-1$ + + public static final String EXTENSION_PROJECT_CHANGED_EVENT_LISTENERS = IMavenConstants.PLUGIN_ID + ".mavenProjectChangedListeners"; //$NON-NLS-1$ + + private static final String ELEMENT_LOCAL_ARCHETYPE = "local"; //$NON-NLS-1$ + + private static final String ELEMENT_REMOTE_ARCHETYPE = "remote"; //$NON-NLS-1$ + + private static final String ATTR_NAME = "name"; //$NON-NLS-1$ + + private static final String ATTR_URL = "url"; //$NON-NLS-1$ + + private static final String ATTR_DESCRIPTION = "description"; //$NON-NLS-1$ + + private static final String ELEMENT_LISTENER = "listener"; //$NON-NLS-1$ + + public static List<ArchetypeCatalogFactory> readArchetypeExtensions() { + List<ArchetypeCatalogFactory> archetypeCatalogs = new ArrayList<ArchetypeCatalogFactory>(); + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint archetypesExtensionPoint = registry.getExtensionPoint(EXTENSION_ARCHETYPES); + if(archetypesExtensionPoint != null) { + IExtension[] archetypesExtensions = archetypesExtensionPoint.getExtensions(); + for(IExtension extension : archetypesExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + IContributor contributor = extension.getContributor(); + for(IConfigurationElement element : elements) { + ArchetypeCatalogFactory factory = readArchetypeCatalogs(element, contributor); + // archetypeManager.addArchetypeCatalogFactory(factory); + archetypeCatalogs.add(factory); + } + } + } + return archetypeCatalogs; + } + + private static ArchetypeCatalogFactory readArchetypeCatalogs(IConfigurationElement element, IContributor contributor) { + if(ELEMENT_LOCAL_ARCHETYPE.equals(element.getName())) { + String name = element.getAttribute(ATTR_NAME); + if(name != null) { + Bundle[] bundles = Platform.getBundles(contributor.getName(), null); + URL catalogUrl = null; + for(int i = 0; i < bundles.length; i++ ) { + Bundle bundle = bundles[i]; + catalogUrl = bundle.getEntry(name); + if(catalogUrl != null) { + String description = element.getAttribute(ATTR_DESCRIPTION); + String url = catalogUrl.toString(); + // XXX ARCHETYPE-161: RemoteCatalogArchetypeDataSource don't allow to download arbitrary urls + return new ArchetypeCatalogFactory.RemoteCatalogFactory(url.substring(0, url.lastIndexOf("/")), //$NON-NLS-1$ + description, false); + } + } + MavenLogger.log("Unable to find Archetype catalog " + name + " in " + contributor.getName(), null); + } + } else if(ELEMENT_REMOTE_ARCHETYPE.equals(element.getName())) { + String url = element.getAttribute(ATTR_URL); + if(url != null) { + String description = element.getAttribute(ATTR_DESCRIPTION); + return new ArchetypeCatalogFactory.RemoteCatalogFactory(url, description, false); + } + } + return null; + } + + public static List<IMavenProjectChangedListener> readProjectChangedEventListenerExtentions() { + ArrayList<IMavenProjectChangedListener> listeners = new ArrayList<IMavenProjectChangedListener>(); + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint mappingsExtensionPoint = registry.getExtensionPoint(EXTENSION_PROJECT_CHANGED_EVENT_LISTENERS); + if(mappingsExtensionPoint != null) { + IExtension[] mappingsExtensions = mappingsExtensionPoint.getExtensions(); + for(IExtension extension : mappingsExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_LISTENER)) { + try { + listeners.add( (IMavenProjectChangedListener) element.createExecutableExtension("class") ); //$NON-NLS-1$ + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + + return listeners; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java new file mode 100644 index 00000000..68a155d7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/Messages.java @@ -0,0 +1,1024 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal; + +import org.eclipse.osgi.util.NLS; + + +/** + * Messages + * + * @author mkleint + */ +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.m2e.core.internal.messages"; //$NON-NLS-1$ + + public static String AbstractProjectConfigurator_error_missing_nature; + + public static String AbstractTransferListenerAdapter_4; + + public static String AbstractTransferListenerAdapter_byte; + + public static String AbstractTransferListenerAdapter_cancelled; + + public static String AbstractTransferListenerAdapter_kb; + + public static String AbstractTransferListenerAdapter_mb; + + public static String AbstractTransferListenerAdapter_subtask; + + public static String AddDependencyAction_error_msg; + + public static String AddDependencyAction_error_title; + + public static String AddDependencyAction_searchDialog_title; + + public static String AddDependencyDialog_artifactId_label; + + public static String AddDependencyDialog_groupId_label; + + public static String AddDependencyDialog_info_label; + + public static String AddDependencyDialog_info_transitive; + + public static String AddDependencyDialog_itemsSelected; + public static String AddDependencyDialog_itemSelected; + + public static String AddDependencyDialog_multipleValuesSelected; + + public static String AddDependencyDialog_results_label; + + public static String AddDependencyDialog_scope_label; + + public static String AddDependencyDialog_search_label; + + public static String AddDependencyDialog_search_message; + + public static String AddDependencyDialog_searchDone; + + public static String AddDependencyDialog_searchError; + + public static String AddDependencyDialog_searching; + + public static String AddDependencyDialog_searchingFor; + + public static String AddDependencyDialog_title; + + public static String AddDependencyDialog_tooManyResults; + + public static String AddDependencyDialog_transitive_dependency; + + public static String AddDependencyDialog_version_label; + + public static String AddPluginAction_searchDialog_title; + + public static String ArchetypeCatalogFactory_default_local; + + public static String ArchetypeCatalogFactory_error_missing_catalog; + + public static String ArchetypeCatalogFactory_indexer_catalog; + + public static String ArchetypeCatalogFactory_internal; + + public static String ArchetypeCatalogFactory_local; + + public static String ArchetypeCatalogFactory_remote; + + public static String ArchetypeCatalogsWriter_error_parse; + + public static String ArchetypeCatalogsWriter_error_write; + + public static String AsyncFetcher_error_cancelled; + + public static String AsyncFetcher_error_server; + + public static String AsyncFetcher_task_fetching; + + public static String AsyncFetcher_task_fetching2; + + public static String ChangeNatureAction_job_changing; + + public static String ChangeNatureAction_status_error; + + public static String CustomArchetypeDialog_error_artid; + + public static String CustomArchetypeDialog_error_grid; + + public static String CustomArchetypeDialog_error_version; + + public static String CustomArchetypeDialog_lblArchetypeartifactid; + + public static String CustomArchetypeDialog_lblArchetypegroupId; + + public static String CustomArchetypeDialog_lblArchetypeversion; + + public static String CustomArchetypeDialog_lblRepo; + + public static String CustomArchetypeDialog_message; + + public static String CustomizableLifecycleMappingPropertyPage_message; + + public static String CustomRepositoriesNode_name; + + public static String DefaultMavenMenuCreator_action_ci; + + public static String DefaultMavenMenuCreator_action_dependency; + + public static String DefaultMavenMenuCreator_action_disable_management; + + public static String DefaultMavenMenuCreator_action_disable_workspace; + + public static String DefaultMavenMenuCreator_action_enable_dm; + + public static String DefaultMavenMenuCreator_action_enable_workspace; + + public static String DefaultMavenMenuCreator_action_issues; + + public static String DefaultMavenMenuCreator_action_open_pom; + + public static String DefaultMavenMenuCreator_action_plugin; + + public static String DefaultMavenMenuCreator_action_project; + + public static String DefaultMavenMenuCreator_action_project_page; + + public static String DefaultMavenMenuCreator_action_scm; + + public static String DefaultMavenMenuCreator_action_update_config; + + public static String DefaultMavenMenuCreator_action_update_deps; + + public static String DefaultMavenMenuCreator_action_update_snapshots; + + public static String EclipseLogger_debug1; + + public static String EclipseLogger_debug2; + + public static String EclipseLogger_error1; + + public static String EclipseLogger_error2; + + public static String EclipseLogger_fatal1; + + public static String EclipseLogger_fatal2; + + public static String EclipseLogger_info1; + + public static String EclipseLogger_info2; + + public static String EclipseLogger_name; + + public static String EclipseLogger_warn1; + + public static String EclipseLogger_warn2; + + public static String EditDependencyDialog_artifactId_label; + + public static String EditDependencyDialog_classifier_label; + + public static String EditDependencyDialog_groupId_label; + + public static String EditDependencyDialog_optional_checkbox; + + public static String EditDependencyDialog_scope_label; + + public static String EditDependencyDialog_systemPath_label; + + public static String EditDependencyDialog_title; + + public static String EditDependencyDialog_type_label; + + public static String EditDependencyDialog_version_label; + + public static String EmptyLifecycleMappingPropertyPage_title; + + public static String EnableNatureAction_job_enable; + + public static String EnableNatureAction_wizard_shell; + + public static String ExtensionReader_foundLifecycleMapping; + + public static String GlobalRepositoriesNode_name; + + public static String IndexedArtifactNode_no_pack; + + public static String IndexUpdaterJob_title; + + public static String LifecycleConfigurationMojoExecutionNotCovered; + + public static String LifecycleMissing; + + public static String LocalArchetypeCatalogDialog_btnBrowse; + + public static String LocalArchetypeCatalogDialog_dialog_title; + + public static String LocalArchetypeCatalogDialog_error_empty; + + public static String LocalArchetypeCatalogDialog_error_exist; + + public static String LocalArchetypeCatalogDialog_error_no_location; + + public static String LocalArchetypeCatalogDialog_lblCatalog; + + public static String LocalArchetypeCatalogDialog_lblDesc; + + public static String LocalArchetypeCatalogDialog_message; + + public static String LocalArchetypeCatalogDialog_title; + + public static String LocalProjectScanner_task_scanning; + + public static String LocalRepositoryNode_local; + + public static String LocalRepositoryRootNode_name; + + public static String M2EErrorDialog_column_error; + + public static String M2EErrorDialog_column_name; + + public static String MavenArchetypesPreferencePage_btnAddLocal; + + public static String MavenArchetypesPreferencePage_btnAddRemote; + + public static String MavenArchetypesPreferencePage_btnEdit; + + public static String MavenArchetypesPreferencePage_btnRemove; + + public static String MavenArchetypesPreferencePage_error; + + public static String MavenArchetypesPreferencePage_link; + + public static String MavenArchetypesPreferencePage_local; + + public static String MavenArchetypesPreferencePage_packaged; + + public static String MavenArchetypesPreferencePage_remote; + + public static String MavenArchetypesPreferencePage_title; + + public static String MavenCheckoutLocationPage_btnBrowse; + + public static String MavenCheckoutLocationPage_btnCheckout; + + public static String MavenCheckoutLocationPage_btnHead; + + public static String MavenCheckoutLocationPage_btnRevSelect; + + public static String MavenCheckoutLocationPage_description; + + public static String MavenCheckoutLocationPage_error_empty; + + public static String MavenCheckoutLocationPage_error_empty_url; + + public static String MavenCheckoutLocationPage_error_scm_empty; + + public static String MavenCheckoutLocationPage_error_scm_invalid; + + public static String MavenCheckoutLocationPage_error_url_empty; + + public static String MavenCheckoutLocationPage_lblRevision; + + public static String MavenCheckoutLocationPage_lblurl; + + public static String MavenCheckoutLocationPage_title; + + public static String MavenCheckoutOperation_task_checking; + + public static String MavenCheckoutOperation_task_scanning; + + public static String MavenCheckoutWizard_location1; + + public static String MavenCheckoutWizard_location2; + + public static String MavenCheckoutWizard_title; + + public static String MavenConsoleImpl_title; + + public static String MavenConsolePageParticipant_any; + + public static String MavenConsolePageParticipant_error; + + public static String MavenConsoleRemoveAction_tooltip; + + public static String MavenDebugOutputAction_0; + + public static String MavenDependenciesWizardPage_lblArtifacts; + + public static String MavenDependenciesWizardPage_searchDialog_title; + + public static String MavenExternalRuntime_error_cannot_parse; + + public static String MavenExternalRuntime_exc_unsupported; + + public static String MavenExternalRuntime_unknown; + + public static String MavenEmbeddedRuntime_unknown; + + public static String MavenGoalSelectionDialog_btnQualified; + + public static String MavenGoalSelectionDialog_error; + + public static String MavenGoalSelectionDialog_lblSelect; + + public static String MavenGoalSelectionDialog_message; + + public static String MavenImpl_error_calc_build_plan; + + public static String MavenImpl_error_create_repo; + + public static String MavenImpl_error_init_maven; + + public static String MavenImpl_error_lookup; + + public static String MavenImpl_error_missing; + + public static String MavenImpl_error_mojo; + + public static String MavenImpl_error_no_exec_req; + + public static String MavenImpl_error_param; + + public static String MavenImpl_error_read_config; + + public static String MavenImpl_error_read_lastUpdated; + + public static String MavenImpl_error_read_pom; + + public static String MavenImpl_error_read_project; + + public static String MavenImpl_error_read_settings; + + public static String MavenImpl_error_read_settings2; + + public static String MavenImpl_error_resolve; + + public static String MavenImpl_error_sort; + + public static String MavenImpl_error_write_lastUpdated; + + public static String MavenImpl_error_write_pom; + + public static String MavenImpl_error_write_settings; + + public static String MavenImportWizard_job; + + public static String MavenImportWizard_title; + + public static String MavenImportWizardPage_desc; + + public static String MavenImportWizardPage_inherited; + + public static String MavenImportWizardPage_title; + + public static String MavenInstallationsPreferencePage_btnAdd; + + public static String MavenInstallationsPreferencePage_btnEdit; + + public static String MavenInstallationsPreferencePage_btnGlobalBrowse; + + public static String MavenInstallationsPreferencePage_btnRemove; + + public static String MavenInstallationsPreferencePage_dialog_install_message; + + public static String MavenInstallationsPreferencePage_dialog_install_title; + + public static String MavenInstallationsPreferencePage_dialog_message; + + public static String MavenInstallationsPreferencePage_dialog_title; + + public static String MavenInstallationsPreferencePage_error_global_missing; + + public static String MavenInstallationsPreferencePage_error_global_parse; + + public static String MavenInstallationsPreferencePage_error_message; + + public static String MavenInstallationsPreferencePage_error_title; + + public static String MavenInstallationsPreferencePage_error2_message; + + public static String MavenInstallationsPreferencePage_error3_message; + + public static String MavenInstallationsPreferencePage_error4_message; + + public static String MavenInstallationsPreferencePage_job_updating; + + public static String MavenInstallationsPreferencePage_lblNote1; + + public static String MavenInstallationsPreferencePage_lblNote2; + + public static String MavenInstallationsPreferencePage_link; + + public static String MavenInstallationsPreferencePage_link_global; + + public static String MavenInstallationsPreferencePage_link_open; + + public static String MavenInstallationsPreferencePage_settings; + + public static String MavenInstallationsPreferencePage_settings_install; + + public static String MavenInstallationsPreferencePage_title; + + public static String MavenInstallFileArtifactWizardPage_btnChecksum; + + public static String MavenInstallFileArtifactWizardPage_btnFilename; + + public static String MavenInstallFileArtifactWizardPage_btnGenerate; + + public static String MavenInstallFileArtifactWizardPage_btnPom; + + public static String MavenInstallFileArtifactWizardPage_desc; + + public static String MavenInstallFileArtifactWizardPage_error_artifactid; + + public static String MavenInstallFileArtifactWizardPage_error_groupid; + + public static String MavenInstallFileArtifactWizardPage_error_missing; + + public static String MavenInstallFileArtifactWizardPage_error_missingpom; + + public static String MavenInstallFileArtifactWizardPage_error_no_name; + + public static String MavenInstallFileArtifactWizardPage_error_packaging; + + public static String MavenInstallFileArtifactWizardPage_error_version; + + public static String MavenInstallFileArtifactWizardPage_file_title; + + public static String MavenInstallFileArtifactWizardPage_lblArtifact; + + public static String MavenInstallFileArtifactWizardPage_lblClassifier; + + public static String MavenInstallFileArtifactWizardPage_lblFileName; + + public static String MavenInstallFileArtifactWizardPage_lblgroupid; + + public static String MavenInstallFileArtifactWizardPage_lblPackaging; + + public static String MavenInstallFileArtifactWizardPage_lblPom; + + public static String MavenInstallFileArtifactWizardPage_lblVersion; + + public static String MavenInstallFileArtifactWizardPage_message; + + public static String MavenInstallFileArtifactWizardPage_title; + + public static String MavenInstallFileWizard_error; + + public static String MavenInstallFileWizard_job; + + public static String MavenInstallFileWizard_title; + + public static String MavenMarkerManager_duplicate_groupid; + + public static String MavenMarkerManager_duplicate_version; + + public static String MavenMarkerManager_error_artifact; + + public static String MavenMarkerManager_error_missing; + + public static String MavenMarkerManager_error_noschema; + + public static String MavenMarkerManager_error_offline; + + public static String MavenMarkerManager_managed_title; + + public static String MavenMarkerManager_metadata_resolution; + + public static String MavenMaterializePomWizard_btnCheckout; + + public static String MavenMaterializePomWizard_btnDev; + + public static String MavenMaterializePomWizard_dialog_message; + + public static String MavenMaterializePomWizard_dialog_title; + + public static String MavenMaterializePomWizard_location_message; + + public static String MavenMaterializePomWizard_location_title; + + public static String MavenMaterializePomWizard_title; + + public static String MavenModelManager_error_cannot_load; + + public static String MavenModelManager_error_create; + + public static String MavenModelManager_error_pom_exists; + + public static String MavenModelManager_error_read; + + public static String MavenModelManager_monitor_building; + + public static String MavenModelManager_monitor_reading; + + public static String MavenModuleWizardParentPage_error; + + public static String MavenPlugin_error_jre_message; + + public static String MavenPlugin_error_jre_title; + + public static String MavenPlugin_error_warn_again; + + public static String MavenPomSelectionComponent_btnJavadoc; + + public static String MavenPomSelectionComponent_btnSource; + + public static String MavenPomSelectionComponent_btnTests; + + public static String MavenPomSelectionComponent_detail1; + + public static String MavenPomSelectionComponent_details2; + + public static String MavenPomSelectionComponent_error; + + public static String MavenPomSelectionComponent_lblResults; + + public static String MavenPomSelectionComponent_managed_decoration; + + public static String MavenPomSelectionComponent_nosel; + + public static String MavenPomSelectionComponent_results; + + public static String MavenPomSelectionComponent_search_title; + + public static String MavenPomSelectionComponent_searching; + + public static String MavenPomSelectionComponent_searchJob; + + public static String MavenPomSelectionComponent_selected; + + public static String MavenPomSelectionComponent_toomany; + + public static String MavenPomSelectionComponent_tooshort; + + public static String MavenPomWizard_error_exists; + + public static String MavenPomWizard_error_title; + + public static String MavenPomWizard_status_not_exists; + + public static String MavenPomWizard_task; + + public static String MavenPomWizard_title; + + public static String MavenPomWizardPage_btnBrowse; + + public static String MavenPomWizardPage_desc; + + public static String MavenPomWizardPage_dialog_title; + + public static String MavenPomWizardPage_error_artid; + + public static String MavenPomWizardPage_error_folder; + + public static String MavenPomWizardPage_error_folder_write; + + public static String MavenPomWizardPage_error_folder2; + + public static String MavenPomWizardPage_error_grid; + + public static String MavenPomWizardPage_error_pack; + + public static String MavenPomWizardPage_error_version; + + public static String MavenPomWizardPage_lblProject; + + public static String MavenPomWizardPage_title; + + public static String MavenPreferencePage_download; + + public static String MavenPreferencePage_hide; + + public static String MavenPreferencePage_select; + + public static String MavenPreferencePage_select2; + + public static String MavenPreferencePage_update; + + public static String MavenProjectCheckoutJob_confirm_message; + + public static String MavenProjectCheckoutJob_confirm_title; + + public static String MavenProjectCheckoutJob_confirm2_message; + + public static String MavenProjectCheckoutJob_confirm2_title; + + public static String MavenProjectCheckoutJob_job; + + public static String MavenProjectCheckoutJob_title; + + public static String MavenProjectFacade_error; + + public static String MavenProjectLifecycleMappingPage_error_no_page; + + public static String MavenProjectLifecycleMappingPage_error_no_strategy; + + public static String MavenProjectLifecycleMappingPage_error_page_error; + + public static String MavenProjectPomScanner_23; + + public static String MavenProjectPomScanner_task_resolving; + + public static String MavenProjectPreferencePage_btnResolve; + + public static String MavenProjectPreferencePage_dialog_message; + + public static String MavenProjectPreferencePage_dialog_title; + + public static String MavenProjectPreferencePage_job; + + public static String MavenProjectPreferencePage_lblProfiles; + + public static String MavenProjectPreferencePage_title; + + public static String MavenProjectWizardArchetypePage_add_title; + + public static String MavenProjectWizardArchetypePage_all; + + public static String MavenProjectWizardArchetypePage_btnAdd; + + public static String MavenProjectWizardArchetypePage_btnConfigure; + + public static String MavenProjectWizardArchetypePage_btnLast; + + public static String MavenProjectWizardArchetypePage_btnSnapshots; + + public static String MavenProjectWizardArchetypePage_error_no; + + public static String MavenProjectWizardArchetypePage_error_read; + + public static String MavenProjectWizardArchetypePage_error_resolve; + + public static String MavenProjectWizardArchetypePage_error_resolve2; + + public static String MavenProjectWizardArchetypePage_lblCatalog; + + public static String MavenProjectWizardArchetypePage_lblFilter; + + public static String MavenProjectWizardArchetypePage_task_downloading; + + public static String MavenProjectWizardArchetypePage_task_indexing; + + public static String MavenProjectWizardArchetypePage_task_reading; + + public static String MavenProjectWizardArchetypePage_task_resolving; + + public static String MavenProjectWizardArchetypePage_task_resolving2; + + public static String MavenProjectWizardArchetypeParametersPage_btnAdd; + + public static String MavenProjectWizardArchetypeParametersPage_btnRemove; + + public static String MavenProjectWizardArchetypeParametersPage_columnName; + + public static String MavenProjectWizardArchetypeParametersPage_columnValue; + + public static String MavenProjectWizardArchetypeParametersPage_error_download; + + public static String MavenProjectWizardArchetypeParametersPage_error_package; + + public static String MavenProjectWizardArchetypeParametersPage_lblProps; + + public static String MavenProjectWizardArchetypeParametersPage_task; + + public static String MavenProjectWizardArtifactPage_searchDialog_title; + + public static String MavenProjectWizardLocationPage_btnLocation; + + public static String MavenProjectWizardLocationPage_btnUserDefault; + + public static String MavenProjectWizardLocationPage_dialog_location; + + public static String MavenProjectWizardLocationPage_lblLocation; + + public static String MavenRepositorySearchDialog_7; + + public static String MavenRepositorySearchDialog_lblScope; + + public static String MavenRepositoryView_action_copy; + + public static String MavenRepositoryView_action_copy_tooltip; + + public static String MavenRepositoryView_action_disable_tooltip; + + public static String MavenRepositoryView_action_enable_tooltip; + + public static String MavenRepositoryView_action_enableFull_tooltip; + + public static String MavenRepositoryView_action_materialize; + + public static String MavenRepositoryView_action_open; + + public static String MavenRepositoryView_action_open_tooltip; + + public static String MavenRepositoryView_action_rebuild; + + public static String MavenRepositoryView_action_rebuild_tooltip; + + public static String MavenRepositoryView_action_reload; + + public static String MavenRepositoryView_action_update; + + public static String MavenRepositoryView_btnCollapse; + + public static String MavenRepositoryView_btnCollapse_tooltip; + + public static String MavenRepositoryView_btnUpdate_tooltip; + + public static String MavenRepositoryView_details_disabled; + + public static String MavenRepositoryView_disable_details; + + public static String MavenRepositoryView_enable_full; + + public static String MavenRepositoryView_enable_minimum; + + public static String MavenRepositoryView_enabled_full; + + public static String MavenRepositoryView_error_message; + + public static String MavenRepositoryView_error_title; + + public static String MavenRepositoryView_job_reloading; + + public static String MavenRepositoryView_minimum_enabled; + + public static String MavenRepositoryView_rebuild_many; + + public static String MavenRepositoryView_rebuild_msg; + + public static String MavenRepositoryView_rebuild_msg2; + + public static String MavenRepositoryView_rebuild_one; + + public static String MavenRepositoryView_rebuild_title; + + public static String MavenRepositoryView_rebuild_title2; + + public static String MavenRepositoryView_reload_msg; + + public static String MavenRepositoryView_reload_title; + + public static String MavenRepositoryView_update_more; + + public static String MavenRepositoryView_update_one; + + public static String MavenSearchPage_btnBrowse; + + public static String MavenSearchPage_btnSelect; + + public static String MavenSearchPage_btnUnselect; + + public static String MavenSearchPage_lblArtifactid; + + public static String MavenSearchPage_lblClass; + + public static String MavenSearchPage_lblGroupid; + + public static String MavenSearchPage_lblPackaging; + + public static String MavenSearchPage_lblRepos; + + public static String MavenSearchPage_lblSha; + + public static String MavenSearchPage_lblVersion; + + public static String MavenSearchPage_separator; + + public static String MavenSettingsPreferencePage_btnBrowse; + + public static String MavenSettingsPreferencePage_btnUpdate; + + public static String MavenSettingsPreferencePage_error_missing; + + public static String MavenSettingsPreferencePage_error_parse; + + public static String MavenSettingsPreferencePage_job_indexing; + + public static String MavenSettingsPreferencePage_job_updating; + + public static String MavenSettingsPreferencePage_lblLocal; + + public static String MavenSettingsPreferencePage_link_tooltip; + + public static String MavenSettingsPreferencePage_link1; + + public static String MavenSettingsPreferencePage_link2; + + public static String MavenSettingsPreferencePage_task_updating; + + public static String MavenSettingsPreferencePage_title; + + public static String MissingLifecycleMapping_name; + public static String MissingLifecycleMappingPropertyPage_error; + + public static String MissingLifecycleMappingPropertyPage_title; + + public static String NexusIndexManager_78; + + public static String NexusIndexManager_error_add_repo; + + public static String NexusIndexManager_error_read_index; + + public static String NexusIndexManager_error_reindexing; + + public static String NexusIndexManager_error_root_grp; + + public static String NexusIndexManager_error_search; + + public static String NexusIndexManager_error_unexpected; + + public static String NexusIndexManager_error_write_index; + + public static String NexusIndexManager_inherited; + + public static String NexusIndexManager_task_updating; + + public static String OpenPomAction_33; + + public static String OpenPomAction_error_download; + + public static String OpenPomAction_error_download_source; + + public static String OpenPomAction_error_open_editor; + + public static String OpenPomAction_error_open_pom; + + public static String OpenPomAction_job_opening; + + public static String OpenPomAction_open_error_message; + + public static String OpenPomAction_open_error_title; + + public static String OpenPomAction_open_title; + + public static String OpenPomAction_title_pom; + + public static String OpenUrlAction_browser_title; + + public static String OpenUrlAction_error_no_ci; + + public static String OpenUrlAction_error_no_issues; + + public static String OpenUrlAction_error_no_scm; + + public static String OpenUrlAction_error_no_url; + + public static String OpenUrlAction_error_open; + + public static String OpenUrlAction_job_browser; + + public static String OpenUrlAction_open_url_message; + + public static String OpenUrlAction_open_url_title; + + public static String PomFileContentDescriber_error; + + public static String ProjectConfigurationManager_0; + + public static String ProjectConfigurationManager_error_failed; + + public static String ProjectConfigurationManager_error_rename; + + public static String ProjectConfigurationManager_error_resolve; + + public static String ProjectConfigurationManager_error_resolve2; + + public static String ProjectConfigurationManager_error_targetDir; + + public static String ProjectConfigurationManager_error_unable_archetype; + + public static String ProjectConfigurationManager_task_configuring; + + public static String ProjectConfigurationManager_task_creating; + + public static String ProjectConfigurationManager_task_creating_folders; + + public static String ProjectConfigurationManager_task_creating_pom; + + public static String ProjectConfigurationManager_task_creating_project; + + public static String ProjectConfigurationManager_task_creating_project1; + + public static String ProjectConfigurationManager_task_creating_workspace; + + public static String ProjectConfigurationManager_task_disable_nature; + + public static String ProjectConfigurationManager_task_enable_nature; + + public static String ProjectConfigurationManager_task_executing_archetype; + + public static String ProjectConfigurationManager_task_importing; + + public static String ProjectConfigurationManager_task_importing2; + + public static String ProjectConfigurationManager_task_refreshing; + + public static String ProjectConfigurationManager_task_updating; + + public static String ProjectConfiguratorsTable_column_id; + + public static String ProjectConfiguratorsTable_column_name; + + public static String ProjectConfiguratorsTableContentProvider_no_configs; + + public static String ProjectRegistryManager_task_project; + + public static String ProjectRegistryManager_task_refreshing; + + public static String ProjectRegistryRefreshJob_task_refreshing; + + public static String ProjectRegistryRefreshJob_title; + + public static String ProjectRepositoriesNode_name; + + public static String ProjectsImportPage_btnDeselect; + + public static String ProjectsImportPage_btnRefresh; + + public static String ProjectsImportPage_btnSelect; + + public static String ProjectsImportPage_desc; + + public static String ProjectsImportPage_dialog_title; + + public static String ProjectsImportPage_error_creation; + + public static String ProjectsImportPage_lstProjects; + + public static String ProjectsImportPage_message; + + public static String ProjectsImportPage_overwrite; + + public static String ProjectsImportPage_overwrite2; + + public static String ProjectsImportPage_task_checking; + + public static String ProjectsImportPage_task_creating; + + public static String ProjectsImportPage_task_processing; + + public static String ProjectsImportPage_task_search; + + public static String ProjectsImportPage_title; + + public static String ProjectsImportWizard_title; + + public static String RemoteArchetypeCatalogDialog_btnVerify; + + public static String RemoteArchetypeCatalogDialog_error_empty; + + public static String RemoteArchetypeCatalogDialog_error_read; + + public static String RemoteArchetypeCatalogDialog_error_required; + + public static String RemoteArchetypeCatalogDialog_job_download; + + public static String RemoteArchetypeCatalogDialog_lblCatalog; + + public static String RemoteArchetypeCatalogDialog_lblDesc; + + public static String RemoteArchetypeCatalogDialog_message; + + public static String RemoteArchetypeCatalogDialog_message_found; + + public static String RemoteArchetypeCatalogDialog_title; + + public static String RepositoryNode_updating; + + public static String RepositoryRegistryUpdateJob_title; + + public static String ScmUrl_error; + + public static String SelectionUtil_error_cannot_read; + public static String UpdateSourcesAction_error_cannot_update; + + public static String UpdateSourcesAction_error_message; + + public static String UpdateSourcesAction_error_title; + + public static String UpdateSourcesAction_job_update_conf; + + public static String WorkingSetGroup_btnAddSet; + + public static String WorkingSetGroup_btnMore; + + public static String WorkingSetGroup_lblSet; + + public static String WorkspaceRepositoryNode_name; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/actions/DefaultMavenMenuCreator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/actions/DefaultMavenMenuCreator.java new file mode 100644 index 00000000..40221b63 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/actions/DefaultMavenMenuCreator.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.actions; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.AbstractMavenMenuCreator; +import org.eclipse.m2e.core.actions.AddDependencyAction; +import org.eclipse.m2e.core.actions.AddPluginAction; +import org.eclipse.m2e.core.actions.ChangeNatureAction; +import org.eclipse.m2e.core.actions.DisableNatureAction; +import org.eclipse.m2e.core.actions.EnableNatureAction; +import org.eclipse.m2e.core.actions.ModuleProjectWizardAction; +import org.eclipse.m2e.core.actions.OpenPomAction; +import org.eclipse.m2e.core.actions.OpenUrlAction; +import org.eclipse.m2e.core.actions.RefreshMavenModelsAction; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.actions.UpdateConfigurationAction; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.ResolverConfiguration; + + +/** + * Default Maven menu creator + * + * @author Eugene Kuleshov + */ +public class DefaultMavenMenuCreator extends AbstractMavenMenuCreator { + + public void createMenu(IMenuManager mgr) { + int selectionType = SelectionUtil.getSelectionType(selection); + if(selectionType == SelectionUtil.UNSUPPORTED) { + return; + } + + if(selection.size() == 1 && selectionType == SelectionUtil.POM_FILE) { + mgr.appendToGroup(NEW, getAction(new AddDependencyAction(), // + AddDependencyAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_dependency)); + mgr.appendToGroup(NEW, getAction(new AddPluginAction(), AddPluginAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_plugin)); + mgr.appendToGroup(NEW, getAction(new ModuleProjectWizardAction(), // + ModuleProjectWizardAction.ID, Messages.getString("action.moduleProjectWizardAction"))); //$NON-NLS-1$ + + mgr.prependToGroup(OPEN, new Separator()); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_PROJECT), // + OpenUrlAction.ID_PROJECT, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_project_page)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_ISSUES), // + OpenUrlAction.ID_ISSUES, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_issues)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_SCM), // + OpenUrlAction.ID_SCM, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_scm)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_CI), // + OpenUrlAction.ID_CI, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_ci)); + } + + if(selectionType == SelectionUtil.PROJECT_WITHOUT_NATURE) { + mgr.appendToGroup(NATURE, getAction(new EnableNatureAction(), // + EnableNatureAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_enable_dm)); + } + + if(selectionType == SelectionUtil.PROJECT_WITH_NATURE) { + if(selection.size() == 1) { + mgr.appendToGroup(NEW, getAction(new AddDependencyAction(), AddDependencyAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_dependency)); + mgr.appendToGroup(NEW, getAction(new AddPluginAction(), AddPluginAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_plugin)); + mgr.appendToGroup(NEW, getAction(new ModuleProjectWizardAction(), // + ModuleProjectWizardAction.ID, Messages.getString("action.moduleProjectWizardAction"))); //$NON-NLS-1$ + mgr.prependToGroup(UPDATE, new Separator()); + } + + + mgr.appendToGroup(UPDATE, getAction(new RefreshMavenModelsAction(), RefreshMavenModelsAction.ID, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_deps, "icons/update_dependencies.gif")); //$NON-NLS-2$ + mgr.appendToGroup(UPDATE, getAction(new RefreshMavenModelsAction(true), RefreshMavenModelsAction.ID_SNAPSHOTS, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_snapshots)); + mgr.appendToGroup(UPDATE, getAction(new UpdateConfigurationAction(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()), // + UpdateConfigurationAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_config, "icons/update_source_folders.gif")); //$NON-NLS-2$ + + mgr.prependToGroup(OPEN, new Separator()); + mgr.appendToGroup(OPEN, getAction(new OpenPomAction(), OpenPomAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_open_pom)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_PROJECT), // + OpenUrlAction.ID_PROJECT, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_project)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_ISSUES), OpenUrlAction.ID_ISSUES, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_issues)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_SCM), OpenUrlAction.ID_SCM, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_scm)); + mgr.appendToGroup(OPEN, getAction(new OpenUrlAction(OpenUrlAction.ID_CI), OpenUrlAction.ID_CI, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_ci)); + + boolean enableWorkspaceResolution = true; + if(selection.size() == 1) { + IProject project = SelectionUtil.getType(selection.getFirstElement(), IProject.class); + if(project != null) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + IMavenProjectFacade projectFacade = projectManager.create(project, new NullProgressMonitor()); + if(projectFacade != null) { + ResolverConfiguration configuration = projectFacade.getResolverConfiguration(); + enableWorkspaceResolution = !configuration.shouldResolveWorkspaceProjects(); + } + } + } + + mgr.prependToGroup(NATURE, new Separator()); + if(enableWorkspaceResolution) { + mgr.appendToGroup(NATURE, getAction(new ChangeNatureAction(ChangeNatureAction.ENABLE_WORKSPACE), + ChangeNatureAction.ID_ENABLE_WORKSPACE, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_enable_workspace)); + } else { + mgr.appendToGroup(NATURE, getAction(new ChangeNatureAction(ChangeNatureAction.DISABLE_WORKSPACE), + ChangeNatureAction.ID_DISABLE_WORKSPACE, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_disable_workspace)); + } + + mgr.appendToGroup(NATURE, getAction(new DisableNatureAction(), // + DisableNatureAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_disable_management)); + } + + if(selectionType == SelectionUtil.WORKING_SET) { + mgr.appendToGroup(UPDATE, getAction(new RefreshMavenModelsAction(), RefreshMavenModelsAction.ID, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_deps, "icons/update_dependencies.gif")); //$NON-NLS-2$ + mgr.appendToGroup(UPDATE, getAction(new RefreshMavenModelsAction(true), RefreshMavenModelsAction.ID_SNAPSHOTS, + org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_snapshots)); + mgr.appendToGroup(UPDATE, getAction(new UpdateConfigurationAction(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()), // + UpdateConfigurationAction.ID, org.eclipse.m2e.core.internal.Messages.DefaultMavenMenuCreator_action_update_config, "icons/update_source_folders.gif")); //$NON-NLS-2$ + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/InternalBuildParticipant.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/InternalBuildParticipant.java new file mode 100644 index 00000000..7a78ff52 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/InternalBuildParticipant.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.builder; + +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.execution.MavenSession; + +import org.sonatype.plexus.build.incremental.BuildContext; + +import org.eclipse.m2e.core.builder.AbstractEclipseBuildContext; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + +public abstract class InternalBuildParticipant { + + private IMavenProjectFacade facade; + private MavenBuilder.GetDeltaCallback getDeltaCallback; +// private BuildContext buildContext; + private MavenSession session; + private AbstractEclipseBuildContext buildContext; + + protected IMavenProjectFacade getMavenProjectFacade() { + return facade; + } + + void setMavenProjectFacade(IMavenProjectFacade facade) { + this.facade = facade; + } + + protected IResourceDelta getDelta(IProject project) { + return getDeltaCallback.getDelta(project); + } + + void setGetDeltaCallback(MavenBuilder.GetDeltaCallback getDeltaCallback) { + this.getDeltaCallback = getDeltaCallback; + } + + protected MavenSession getSession() { + return session; + } + + void setSession(MavenSession session) { + this.session = session; + } + + public abstract Set<IProject> build(int kind, IProgressMonitor monitor) throws Exception; + + @SuppressWarnings("unused") + public void clean(IProgressMonitor monitor) throws CoreException { + // default implementation does nothing + } + + public abstract boolean callOnEmptyDelta(); + + void setBuildContext(AbstractEclipseBuildContext buildContext) { + this.buildContext = buildContext; + } + + protected BuildContext getBuildContext() { + return buildContext; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenBuilder.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenBuilder.java new file mode 100644 index 00000000..0e13bd10 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenBuilder.java @@ -0,0 +1,302 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.builder; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.QualifiedName; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +import org.sonatype.plexus.build.incremental.ThreadBuildContext; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.builder.AbstractEclipseBuildContext; +import org.eclipse.m2e.core.builder.EclipseBuildContext; +import org.eclipse.m2e.core.builder.EclipseIncrementalBuildContext; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.util.M2EUtils; + + +public class MavenBuilder extends IncrementalProjectBuilder { + + public static boolean DEBUG = MavenPlugin.getDefault().isDebugging() + & Boolean.parseBoolean(Platform.getDebugOption(IMavenConstants.PLUGIN_ID + "/debug/builder")); //$NON-NLS-1$ + + public static QualifiedName BUILD_CONTEXT_KEY = new QualifiedName(IMavenConstants.PLUGIN_ID, "BuildContext"); //$NON-NLS-1$ + + static interface GetDeltaCallback { + public IResourceDelta getDelta(IProject project); + } + + private GetDeltaCallback getDeltaCallback = new GetDeltaCallback() { + public IResourceDelta getDelta(IProject project) { + return MavenBuilder.this.getDelta(project); + } + }; + + /* + * @see org.eclipse.core.internal.events.InternalBuilder#build(int, + * java.util.Map, org.eclipse.core.runtime.IProgressMonitor) + */ + @SuppressWarnings("unchecked") + protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenConsole console = plugin.getConsole(); + MavenProjectManager projectManager = plugin.getMavenProjectManager(); + IProjectConfigurationManager configurationManager = plugin.getProjectConfigurationManager(); + IMavenConfiguration mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + IMavenMarkerManager markerManager = plugin.getMavenMarkerManager(); + + IProject project = getProject(); + markerManager.deleteMarkers(project, IMavenConstants.MARKER_BUILD_ID); + + if(project.hasNature(IMavenConstants.NATURE_ID)) { + IFile pomResource = project.getFile(IMavenConstants.POM_FILE_NAME); + if(pomResource == null) { + console.logError("Project " + project.getName() + " does not have pom.xml"); + return null; + } + + IMavenProjectFacade projectFacade = projectManager.create(getProject(), monitor); + if(projectFacade == null) { + // XXX is this really possible? should we warn the user? + return null; + } + + if (projectFacade.isStale()) { + MavenUpdateRequest updateRequest = new MavenUpdateRequest(project, mavenConfiguration.isOffline() /*offline*/, false /*updateSnapshots*/); + projectManager.refresh(updateRequest, monitor); + IMavenProjectFacade facade = projectManager.create(project, monitor); + if(facade == null){ + // error marker should have been created + return null; + } + } + + IResourceDelta delta = getDelta(project); + AbstractEclipseBuildContext buildContext; + Map<String, Object> contextState = (Map<String, Object>) project.getSessionProperty(BUILD_CONTEXT_KEY); + if(contextState != null && (INCREMENTAL_BUILD == kind || AUTO_BUILD == kind)) { + buildContext = new EclipseIncrementalBuildContext(delta, contextState); + } else { + // must be full build + contextState = new HashMap<String, Object>(); + project.setSessionProperty(BUILD_CONTEXT_KEY, contextState); + buildContext = new EclipseBuildContext(project, contextState); + } + + Set<IProject> dependencies = new HashSet<IProject>(); + + IMaven maven = MavenPlugin.getDefault().getMaven(); + MavenExecutionRequest request = projectManager.createExecutionRequest(pomResource, projectFacade.getResolverConfiguration(), monitor); + + MavenProject mavenProject = null; + try{ + mavenProject = projectFacade.getMavenProject(monitor); + } catch(CoreException ce){ + //unable to read the project facade + addErrorMarker(ce); + monitor.done(); + return null; + } + MavenSession session = maven.createSession(request, mavenProject); + ILifecycleMapping lifecycleMapping = configurationManager.getLifecycleMapping(projectFacade, monitor); + + ThreadBuildContext.setThreadBuildContext(buildContext); + try { + if(projectFacade.hasValidConfiguration()) { + List<AbstractBuildParticipant> participants = lifecycleMapping.getBuildParticipants(projectFacade, monitor); + for(InternalBuildParticipant participant : participants) { + participant.setMavenProjectFacade(projectFacade); + participant.setGetDeltaCallback(getDeltaCallback); + participant.setSession(session); + participant.setBuildContext(buildContext); + try { + if(FULL_BUILD == kind || delta != null || participant.callOnEmptyDelta()) { + Set<IProject> sub = participant.build(kind, monitor); + if(sub != null) { + dependencies.addAll(sub); + } + } + } catch(Exception e) { + MavenLogger.log("Exception in build participant", e); + } finally { + participant.setMavenProjectFacade(null); + participant.setGetDeltaCallback(null); + participant.setSession(null); + participant.setBuildContext(null); + } + } + } + } catch (CoreException e) { + addErrorMarker(e); + } finally { + ThreadBuildContext.setThreadBuildContext(null); + } + + for(File file : buildContext.getFiles()) { + IPath path = getProjectRelativePath(project, file); + if(path == null) { + continue; // odd + } + + if(!file.exists()) { + IResource resource = project.findMember(path); + if (resource != null) { + resource.refreshLocal(IResource.DEPTH_INFINITE, monitor); + } + } else if(file.isDirectory()) { + IFolder ifolder = project.getFolder(path); + ifolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); + } else { + IFile ifile = project.getFile(path); + ifile.refreshLocal(IResource.DEPTH_ZERO, monitor); + } + } + + MavenExecutionResult result = session.getResult(); + if (result.hasExceptions()) { + markerManager.addMarkers(pomResource, IMavenConstants.MARKER_BUILD_ID, result); + } + + return !dependencies.isEmpty() ? dependencies.toArray(new IProject[dependencies.size()]) : null; + } + return null; + } + + private void addErrorMarker(Exception e) { + String msg = e.getMessage(); + String rootCause = M2EUtils.getRootCauseMessage(e); + if(!e.equals(msg)){ + msg = msg+": "+rootCause; //$NON-NLS-1$ + } + + MavenPlugin plugin = MavenPlugin.getDefault(); + IMavenMarkerManager markerManager = plugin.getMavenMarkerManager(); + markerManager.addMarker(getProject(), IMavenConstants.MARKER_BUILD_ID, msg, 1, IMarker.SEVERITY_ERROR); + } + + public static IPath getProjectRelativePath(IProject project, File file) { + if(project == null || file == null) { + return null; + } + + IPath projectPath = project.getLocation(); + if(projectPath == null) { + return null; + } + + IPath filePath = new Path(file.getAbsolutePath()); + if(!projectPath.isPrefixOf(filePath)) { + return null; + } + + return filePath.removeFirstSegments(projectPath.segmentCount()); + } + + protected void clean(IProgressMonitor monitor) throws CoreException{ + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenProjectManager projectManager = plugin.getMavenProjectManager(); + IProjectConfigurationManager configurationManager = plugin.getProjectConfigurationManager(); + + IProject project = getProject(); + IMavenMarkerManager markerManager = plugin.getMavenMarkerManager(); + markerManager.deleteMarkers(project, IMavenConstants.MARKER_BUILD_ID); + + if(project.hasNature(IMavenConstants.NATURE_ID)) { + IFile pomResource = project.getFile(IMavenConstants.POM_FILE_NAME); + if(pomResource == null) { + return; + } + + IMavenProjectFacade projectFacade = projectManager.create(getProject(), monitor); + if(projectFacade == null) { + return; + } + + IMaven maven = MavenPlugin.getDefault().getMaven(); + + // TODO flush relevant caches + + project.setSessionProperty(BUILD_CONTEXT_KEY, null); // clean context state + Map<String, Object> contextState = new HashMap<String, Object>(); + EclipseBuildContext buildContext = new EclipseBuildContext(project, contextState); + + MavenExecutionRequest request = projectManager.createExecutionRequest(pomResource, projectFacade.getResolverConfiguration(), monitor); + MavenSession session = null; + try{ + session = maven.createSession(request, projectFacade.getMavenProject(monitor)); + } catch(CoreException ce){ + //the pom cannot be read. don't fill the log full of junk, just add an error marker + addErrorMarker(ce); + return; + } + ILifecycleMapping lifecycleMapping = configurationManager.getLifecycleMapping(projectFacade, monitor); + + ThreadBuildContext.setThreadBuildContext(buildContext); + try { + for (InternalBuildParticipant participant : lifecycleMapping.getBuildParticipants(projectFacade, monitor)) { + participant.setMavenProjectFacade(projectFacade); + participant.setGetDeltaCallback(getDeltaCallback); + participant.setSession(session); + try { + participant.clean(monitor); + } catch (Exception ex) { + // TODO Auto-generated catch block + MavenLogger.log("Totoally unexpected exception", ex); + } finally { + participant.setMavenProjectFacade(null); + participant.setGetDeltaCallback(null); + participant.setSession(null); + } + } + } catch (CoreException e) { + addErrorMarker(e); + } finally { + ThreadBuildContext.setThreadBuildContext(null); + } + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenNature.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenNature.java new file mode 100644 index 00000000..b3f38f3c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/MavenNature.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.builder; + +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IProjectNature; +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.m2e.core.core.IMavenConstants; + + +public class MavenNature implements IProjectNature { + private IProject project; + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#configure() + */ + public void configure() throws CoreException { + IProjectDescription desc = project.getDescription(); + ICommand[] commands = desc.getBuildSpec(); + + for(int i = 0; i < commands.length; ++i) { + if(commands[i].getBuilderName().equals(IMavenConstants.BUILDER_ID)) { + return; + } + } + + ICommand[] newCommands = new ICommand[commands.length + 1]; + System.arraycopy(commands, 0, newCommands, 0, commands.length); + ICommand command = desc.newCommand(); + command.setBuilderName(IMavenConstants.BUILDER_ID); + newCommands[commands.length] = command; + desc.setBuildSpec(newCommands); + project.setDescription(desc, null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#deconfigure() + */ + public void deconfigure() throws CoreException { + IProjectDescription description = getProject().getDescription(); + ICommand[] commands = description.getBuildSpec(); + for(int i = 0; i < commands.length; ++i) { + if(commands[i].getBuilderName().equals(IMavenConstants.BUILDER_ID)) { + ICommand[] newCommands = new ICommand[commands.length - 1]; + System.arraycopy(commands, 0, newCommands, 0, i); + System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1); + description.setBuildSpec(newCommands); + return; + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#getProject() + */ + public IProject getProject() { + return project; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject) + */ + public void setProject(IProject project) { + this.project = project; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/ResourceScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/ResourceScanner.java new file mode 100644 index 00000000..eff507d3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/builder/ResourceScanner.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.builder; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceVisitor; +import org.eclipse.core.runtime.CoreException; + +import org.codehaus.plexus.util.AbstractScanner; + +/** + * WorkspaceScanner + * + * @author igor + */ +public class ResourceScanner extends AbstractScanner { + + protected final IResource resource; + + protected final List<String> includedDirectories = new ArrayList<String>(); + + protected final List<String> includedFiles = new ArrayList<String>(); + + public ResourceScanner(IResource resource) { + this.resource = resource; + } + + public String[] getIncludedDirectories() { + return includedDirectories.toArray(new String[includedDirectories.size()]); + } + + public String[] getIncludedFiles() { + return includedFiles.toArray(new String[includedFiles.size()]); + } + + public void scan() { + try { + setupDefaultFilters(); + scanResource(); + } catch(CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void scanResource() throws CoreException { + resource.accept(new IResourceVisitor() { + + public boolean visit(IResource resource) { + String relpath = getRelativePath(resource); + if (isIncluded(relpath) && !isExcluded(relpath)) { + if (resource instanceof IContainer) { + includedDirectories.add(relpath); + } else { + includedFiles.add(relpath); + } + return true; + } else if (resource instanceof IFolder) { + return couldHoldIncluded(relpath); + } + + return false; + } + + }); + } + + protected String getRelativePath(IResource resource) { + return resource.getFullPath().removeFirstSegments(this.resource.getFullPath().segmentCount()).toOSString(); + } + + public File getBasedir() { + return resource.getLocation().toFile(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/ConsoleDocument.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/ConsoleDocument.java new file mode 100644 index 00000000..77ccfa1b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/ConsoleDocument.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.console; + +/** + * Simple circular buffer that stores a fix number of lines. + */ +// TODO consider use standard ConsoleDocument +public class ConsoleDocument { + public static final int COMMAND = 0; // command text + public static final int MESSAGE = 1; // message received + public static final int ERROR = 2; // error received + public static final int STATUS = 3; // status text + public static final int DELIMITER = 4; // delimiter text between runs + + private int[] lineTypes; + private String[] lines; + + private int writeIndex = 0; + private int readIndex = 0; + + private static final int BUFFER_SIZE = 200; + + protected static class ConsoleLine { + public String line; + public int type; + ConsoleLine(String line, int type) { + this.line = line; + this.type = type; + } + } + + /** + * Creates an empty console document. + */ + public ConsoleDocument() { + } + + /** + * Clears the console document. + */ + public void clear() { + lineTypes = null; + lines = null; + writeIndex = 0; + readIndex = 0; + } + + /** + * Appends a line of the specified type to the end of the console. + */ + public void appendConsoleLine(int type, String line) { + if(lines == null) { + lines = new String[BUFFER_SIZE]; + lineTypes = new int[BUFFER_SIZE]; + } + lines[writeIndex] = line; + lineTypes[writeIndex] = type; + + if(++writeIndex >= BUFFER_SIZE) { + writeIndex = 0; + } + if(writeIndex == readIndex) { + if(++readIndex >= BUFFER_SIZE) { + readIndex = 0; + } + } + } + + public ConsoleLine[] getLines() { + if(isEmpty()) return new ConsoleLine[0]; + ConsoleLine[] docLines = new ConsoleLine[readIndex > writeIndex ? BUFFER_SIZE : writeIndex]; + int index = readIndex; + for (int i = 0; i < docLines.length; i++) { + docLines[i] = new ConsoleLine(lines[index], lineTypes[index]); + if (++index >= BUFFER_SIZE) { + index = 0; + } + } + return docLines; + } + + public boolean isEmpty() { + return writeIndex == readIndex; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/MavenConsoleImpl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/MavenConsoleImpl.java new file mode 100644 index 00000000..0b28a509 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/console/MavenConsoleImpl.java @@ -0,0 +1,404 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.console; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.util.ULocale; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleListener; +import org.eclipse.ui.console.IConsoleManager; +import org.eclipse.ui.console.IOConsole; +import org.eclipse.ui.console.IOConsoleOutputStream; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConsoleListener; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +/** + * Maven Console implementation + * + * @author Dmitri Maximovich + */ +public class MavenConsoleImpl extends IOConsole implements MavenConsole, IPropertyChangeListener { + + private boolean initialized = false; + + // console is visible in the Console view + private boolean visible = false; + + private ConsoleDocument consoleDocument; + + // created colors for each line type - must be disposed at shutdown + private Color commandColor; + + private Color messageColor; + + private Color errorColor; + + // streams for each command type - each stream has its own color + private IOConsoleOutputStream commandStream; + + private IOConsoleOutputStream messageStream; + + private IOConsoleOutputStream errorStream; + private static final String TITLE = Messages.MavenConsoleImpl_title; + + private List<IMavenConsoleListener> listeners = new CopyOnWriteArrayList<IMavenConsoleListener>(); + + public MavenConsoleImpl(ImageDescriptor imageDescriptor) { + super(TITLE, imageDescriptor); + this.setConsoleDocument(new ConsoleDocument()); + } + + protected void init() { + super.init(); + + // Ensure that initialization occurs in the UI thread + Display.getDefault().asyncExec(new Runnable() { + public void run() { + JFaceResources.getFontRegistry().addListener(MavenConsoleImpl.this); + initializeConsoleStreams(Display.getDefault()); + dumpConsole(); + } + }); + } + + /* + * Initialize three streams of the console. Must be called from the UI thread, so synchronization is unnecessary. + */ + protected void initializeConsoleStreams(Display display) { + if(!initialized) { + setCommandStream(newOutputStream()); + setErrorStream(newOutputStream()); + setMessageStream(newOutputStream()); + + // TODO convert this to use themes + // install colors + commandColor = new Color(display, new RGB(0, 0, 0)); + messageColor = new Color(display, new RGB(0, 0, 255)); + errorColor = new Color(display, new RGB(255, 0, 0)); + + getCommandStream().setColor(commandColor); + getMessageStream().setColor(messageColor); + getErrorStream().setColor(errorColor); + + // install font + setFont(JFaceResources.getFontRegistry().get("pref_console_font")); //$NON-NLS-1$ + + initialized = true; + } + } + + /** + * Is always called from main thread, so synchronization not necessary + */ + protected void dumpConsole() { + setVisible(true); + ConsoleDocument.ConsoleLine[] lines = getConsoleDocument().getLines(); + for(int i = 0; i < lines.length; i++ ) { + ConsoleDocument.ConsoleLine line = lines[i]; + appendLine(line.type, line.line); + } + getConsoleDocument().clear(); + } + + private void appendLine(final int type, final String line) { + show(false); + //the synchronization here caused a deadlock. since the writes are simply appending to the output stream + //or the document, just doing it on the main thread to avoid deadlocks and or corruption of the + //document or output stream + Display.getDefault().asyncExec(new Runnable(){ + public void run(){ + if(isVisible()) { + try { + switch(type) { + case ConsoleDocument.COMMAND: + getCommandStream().write(line); + getCommandStream().write('\n'); + break; + case ConsoleDocument.MESSAGE: + getMessageStream().write(line); + getMessageStream().write('\n'); + break; + case ConsoleDocument.ERROR: + getErrorStream().write(line); + getErrorStream().write('\n'); + break; + } + } catch(IOException ex) { + MavenLogger.log("Console error", ex); + } + } else { + getConsoleDocument().appendConsoleLine(type, line); + } + } + }); + } + + /** + * Show the console. + * + * @param showNoMatterWhat ignore preferences if <code>true</code> + */ + public void show(boolean showNoMatterWhat) { + if(showNoMatterWhat) { + if(!isVisible()) { + showConsole(); + } else { + ConsolePlugin.getDefault().getConsoleManager().showConsoleView(this); + } + } + } + + public void showConsole() { + boolean exists = false; + IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager(); + for(IConsole element : manager.getConsoles()) { + if(this == element) { + exists = true; + } + } + if(!exists) { + manager.addConsoles(new IConsole[] {this}); + } + manager.showConsoleView(this); + } + + public void closeConsole() { + IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager(); + manager.removeConsoles(new IConsole[] {this}); + ConsolePlugin.getDefault().getConsoleManager().addConsoleListener(this.newLifecycle()); + } + + + public void propertyChange(PropertyChangeEvent event) { + // font changed + setFont(JFaceResources.getFontRegistry().get("pref_console_font")); //$NON-NLS-1$ + } + + private void bringConsoleToFront() { + if(PlatformUI.isWorkbenchRunning()) { + IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager(); + if(!isVisible()) { + manager.addConsoles(new IConsole[] {this}); + } + manager.showConsoleView(this); + } + } + + // Called when console is removed from the console view + protected void dispose() { + // Here we can't call super.dispose() because we actually want the partitioner to remain + // connected, but we won't show lines until the console is added to the console manager + // again. + Display.getDefault().asyncExec(new Runnable(){ + public void run(){ + setVisible(false); + JFaceResources.getFontRegistry().removeListener(MavenConsoleImpl.this); + } + }); + } + + public void shutdown() { + // Call super dispose because we want the partitioner to be + // disconnected. + super.dispose(); + if(commandColor != null) { + commandColor.dispose(); + } + if(messageColor != null) { + messageColor.dispose(); + } + if(errorColor != null) { + errorColor.dispose(); + } + } + + private DateFormat getDateFormat() { + return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, ULocale.getDefault()); + } + + // MavenConsole + + public void logMessage(String message) { + if(showConsoleOnOutput()){ + bringConsoleToFront(); + } + appendLine(ConsoleDocument.MESSAGE, getDateFormat().format(new Date()) + ": " + message); + + for(IMavenConsoleListener listener : listeners) { + try { + listener.loggingMessage(message); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + public void logError(String message) { + if(showConsoleOnError()){ + bringConsoleToFront(); + } + appendLine(ConsoleDocument.ERROR, getDateFormat().format(new Date()) + ": " + message); //$NON-NLS-1$ + + for(IMavenConsoleListener listener : listeners) { + try { + listener.loggingError(message); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + public boolean showConsoleOnError(){ + return MavenPlugin.getDefault().getPreferenceStore().getBoolean(MavenPreferenceConstants.P_SHOW_CONSOLE_ON_ERR); + } + + public boolean showConsoleOnOutput(){ + return MavenPlugin.getDefault().getPreferenceStore().getBoolean(MavenPreferenceConstants.P_SHOW_CONSOLE_ON_OUTPUT); + } + public IConsoleListener newLifecycle() { + return new MavenConsoleLifecycle(); + } + + /** + * @param commandStream The commandStream to set. + */ + protected void setCommandStream(IOConsoleOutputStream commandStream) { + this.commandStream = commandStream; + } + + /** + * @return Returns the commandStream. + */ + protected IOConsoleOutputStream getCommandStream() { + return commandStream; + } + + /** + * @param messageStream The messageStream to set. + */ + protected void setMessageStream(IOConsoleOutputStream messageStream) { + this.messageStream = messageStream; + } + + /** + * @return Returns the messageStream. + */ + protected IOConsoleOutputStream getMessageStream() { + return messageStream; + } + + /** + * @param errorStream The errorStream to set. + */ + protected void setErrorStream(IOConsoleOutputStream errorStream) { + this.errorStream = errorStream; + } + + /** + * @return Returns the errorStream. + */ + protected IOConsoleOutputStream getErrorStream() { + return errorStream; + } + + /** + * @param visible The visible to set. + */ + protected void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * @return Returns the visible. + */ + protected boolean isVisible() { + return visible; + } + + /** + * @param consoleDocument The consoleDocument to set. + */ + private void setConsoleDocument(ConsoleDocument consoleDocument) { + this.consoleDocument = consoleDocument; + } + + /** + * @return Returns the consoleDocument. + */ + protected ConsoleDocument getConsoleDocument() { + return consoleDocument; + } + + /** + * Used to notify this console of lifecycle methods <code>init()</code> and <code>dispose()</code>. + */ + public class MavenConsoleLifecycle implements org.eclipse.ui.console.IConsoleListener { + + public void consolesAdded(IConsole[] consoles) { + for(int i = 0; i < consoles.length; i++ ) { + IConsole console = consoles[i]; + if(console == MavenConsoleImpl.this) { + init(); + } + } + + } + + public void consolesRemoved(IConsole[] consoles) { + for(int i = 0; i < consoles.length; i++ ) { + IConsole console = consoles[i]; + if(console == MavenConsoleImpl.this) { + ConsolePlugin.getDefault().getConsoleManager().removeConsoleListener(this); + dispose(); + } + } + } + + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.core.MavenConsole#addMavenConsoleListener(org.eclipse.m2e.core.IMavenConsoleListener) + */ + public void addMavenConsoleListener(IMavenConsoleListener listener) { + listeners.remove(listener); + listeners.add(listener); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.core.MavenConsole#removeMavenConsoleListener(org.eclipse.m2e.core.IMavenConsoleListener) + */ + public void removeMavenConsoleListener(IMavenConsoleListener listener) { + listeners.remove(listener); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomFileContentDescriber.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomFileContentDescriber.java new file mode 100644 index 00000000..49c9a868 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomFileContentDescriber.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.content; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import org.eclipse.core.runtime.content.IContentDescription; + +import org.eclipse.m2e.core.internal.Messages; + + +/** + * A content describer for POM files. + * + * @see org.eclipse.ant.internal.core.contentDescriber.AntBuildfileContentDescriber + * @author Herve Boutemy + * @since 0.9.6 + */ +public final class PomFileContentDescriber extends XMLContentDescriber { + /** + * Determines the validation status for the given contents. + * + * @param contents the contents to be evaluated + * @return one of the following:<ul> + * <li><code>VALID</code></li>, + * <li><code>INVALID</code></li>, + * <li><code>INDETERMINATE</code></li> + * </ul> + * @throws IOException + */ + private int checkCriteria(InputSource contents) throws IOException { + PomHandler pomHandler = new PomHandler(); + try { + if(!pomHandler.parseContents(contents)) { + return INDETERMINATE; + } + } catch(SAXException e) { + // we may be handed any kind of contents... it is normal we fail to parse + return INDETERMINATE; + } catch(ParserConfigurationException e) { + // some bad thing happened - force this describer to be disabled + throw new RuntimeException(Messages.PomFileContentDescriber_error); + } + + // Check to see if we matched our criteria. + if(pomHandler.hasRootProjectElement()) { + if(pomHandler.hasArtifactIdElement()) { + //project and artifactId element + return VALID; + } + //only a top level project element: maybe a POM file, but maybe an Ant buildfile, a site descriptor, ... + return INDETERMINATE; + } + return INDETERMINATE; + } + + @Override + public int describe(InputStream contents, IContentDescription description) throws IOException { + // call the basic XML describer to do basic recognition + if(super.describe(contents, description) == INVALID) { + return INVALID; + } + // super.describe will have consumed some chars, need to rewind + contents.reset(); + // Check to see if we matched our criteria. + return checkCriteria(new InputSource(contents)); + } + + @Override + public int describe(Reader contents, IContentDescription description) throws IOException { + // call the basic XML describer to do basic recognition + if(super.describe(contents, description) == INVALID) { + return INVALID; + } + // super.describe will have consumed some chars, need to rewind + contents.reset(); + // Check to see if we matched our criteria. + return checkCriteria(new InputSource(contents)); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomHandler.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomHandler.java new file mode 100644 index 00000000..3ff2db65 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/PomHandler.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.content; + +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + + +/** + * An xml event handler for detecting the project top-level element in a POM file. Also records whether a default + * attribute is present for the project and if any typical Maven elements are present. + * + * @see org.eclipse.ant.internal.core.contentDescriber.AntHandler + * @author Herve Boutemy + * @since 0.9.6 + */ +public final class PomHandler extends DefaultHandler { + /** + * An exception indicating that the parsing should stop. + */ + private class StopParsingException extends SAXException { + /** + * All serializable objects should have a stable serialVersionUID + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs an instance of <code>StopParsingException</code> with a <code>null</code> detail message. + */ + public StopParsingException() { + super((String) null); + } + } + + private static final String PROJECT = "project"; //$NON-NLS-1$ + + private static final String ARTIFACTID = "artifactId"; //$NON-NLS-1$ + + /** + * This is the name of the top-level element found in the XML file. This member variable is <code>null</code> unless + * the file has been parsed successful to the point of finding the top-level element. + */ + private String fTopElementFound = null; + + private SAXParserFactory fFactory; + + private boolean fArtifactIdFound = false; + + private int fLevel = -1; + + /** + * Creates a new SAX parser for use within this instance. + * + * @return The newly created parser. + * @throws ParserConfigurationException If a parser of the given configuration cannot be created. + * @throws SAXException If something in general goes wrong when creating the parser. + */ + private final SAXParser createParser(SAXParserFactory parserFactory) throws ParserConfigurationException, + SAXException, SAXNotRecognizedException, SAXNotSupportedException { + // Initialize the parser. + final SAXParser parser = parserFactory.newSAXParser(); + final XMLReader reader = parser.getXMLReader(); + // disable DTD validation + try { + // be sure validation is "off" or the feature to ignore DTD's will not apply + reader.setFeature("http://xml.org/sax/features/validation", false); //$NON-NLS-1$ + reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); //$NON-NLS-1$ + } catch(SAXNotRecognizedException e) { + // not a big deal if the parser does not recognize the features + } catch(SAXNotSupportedException e) { + // not a big deal if the parser does not support the features + } + return parser; + } + + private SAXParserFactory getFactory() { + synchronized(this) { + if(fFactory != null) { + return fFactory; + } + fFactory = SAXParserFactory.newInstance(); + fFactory.setNamespaceAware(true); + } + return fFactory; + } + + protected boolean parseContents(InputSource contents) throws IOException, ParserConfigurationException, SAXException { + // Parse the file into we have what we need (or an error occurs). + try { + fFactory = getFactory(); + if(fFactory == null) { + return false; + } + final SAXParser parser = createParser(fFactory); + // to support external entities specified as relative URIs (see bug 63298) + contents.setSystemId("/"); //$NON-NLS-1$ + parser.parse(contents, this); + } catch(StopParsingException e) { + // Abort the parsing normally. Fall through... + } + return true; + } + + /* + * Resolve external entity definitions to an empty string. This is to speed + * up processing of files with external DTDs. Not resolving the contents + * of the DTD is ok, as only the System ID of the DTD declaration is used. + * @see org.xml.sax.helpers.DefaultHandler#resolveEntity(java.lang.String, java.lang.String) + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); //$NON-NLS-1$ + } + + + @Override + public final void startElement(final String uri, final String elementName, final String qualifiedName, + final Attributes attributes) throws SAXException { + fLevel++ ; + if(fTopElementFound == null) { + fTopElementFound = elementName; + if(!hasRootProjectElement()) { + throw new StopParsingException(); + } + } + if(fLevel == 1 && ARTIFACTID.equals(elementName)) { + fArtifactIdFound = true; + throw new StopParsingException(); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + fLevel-- ; + } + + protected boolean hasRootProjectElement() { + return PROJECT.equals(fTopElementFound); + } + + protected boolean hasArtifactIdElement() { + return fArtifactIdFound; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/TextContentDescriber.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/TextContentDescriber.java new file mode 100644 index 00000000..bfe4da15 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/TextContentDescriber.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.content; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.ITextContentDescriber; + +/** + * A copy of org.eclipse.core.internal.content.TextContentDescriber to avoid internal API use. + * + * This class provides internal basis for text-based content describers. + * + * <p> + * Note: do not add protected/public members to this class if you don't intend to + * make them public API. + * </p> + * + * @see org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 + * @since 3.0 + */ +class TextContentDescriber implements ITextContentDescriber { + + private final static QualifiedName[] SUPPORTED_OPTIONS = {IContentDescription.BYTE_ORDER_MARK}; + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.content.ITextContentDescriber#describe(java.io.Reader, org.eclipse.core.runtime.content.IContentDescription) + */ + @SuppressWarnings("unused") + public int describe(Reader contents, IContentDescription description) throws IOException { + // we want to be pretty loose on detecting the text content type + return INDETERMINATE; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.content.IContentDescriber#describe(java.io.InputStream, org.eclipse.core.runtime.content.IContentDescription) + */ + public int describe(InputStream contents, IContentDescription description) throws IOException { + if (description == null || !description.isRequested(IContentDescription.BYTE_ORDER_MARK)) + return INDETERMINATE; + byte[] bom = getByteOrderMark(contents); + if (bom != null) + description.setProperty(IContentDescription.BYTE_ORDER_MARK, bom); + // we want to be pretty loose on detecting the text content type + return INDETERMINATE; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.content.IContentDescriber#getSupportedOptions() + */ + public QualifiedName[] getSupportedOptions() { + return SUPPORTED_OPTIONS; + } + + byte[] getByteOrderMark(InputStream input) throws IOException { + int first = input.read(); + if (first == 0xEF) { + //look for the UTF-8 Byte Order Mark (BOM) + int second = input.read(); + int third = input.read(); + if (second == 0xBB && third == 0xBF) + return IContentDescription.BOM_UTF_8; + } else if (first == 0xFE) { + //look for the UTF-16 BOM + if (input.read() == 0xFF) + return IContentDescription.BOM_UTF_16BE; + } else if (first == 0xFF) { + if (input.read() == 0xFE) + return IContentDescription.BOM_UTF_16LE; + } + return null; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/XMLContentDescriber.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/XMLContentDescriber.java new file mode 100644 index 00000000..ed115a0a --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/content/XMLContentDescriber.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.content; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.ITextContentDescriber; + +/** + * A copy of org.eclipse.core.internal.content.XMLContentDescriber to avoid internal API use. + * + * A content interpreter for XML files. + * This class provides internal basis for XML-based content describers. + * <p> + * Note: do not add protected/public members to this class if you don't intend to + * make them public API. + * </p> + * + * @see org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 + * @see "http://www.w3.org/TR/REC-xml *" + */ +class XMLContentDescriber extends TextContentDescriber implements ITextContentDescriber { + private static final QualifiedName[] SUPPORTED_OPTIONS = new QualifiedName[] {IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK}; + private static final String ENCODING = "encoding="; //$NON-NLS-1$ + private static final String XML_PREFIX = "<?xml "; //$NON-NLS-1$ + + public int describe(InputStream input, IContentDescription description) throws IOException { + byte[] bom = getByteOrderMark(input); + String xmlDeclEncoding = "UTF-8"; //$NON-NLS-1$ + input.reset(); + if (bom != null) { + if (bom == IContentDescription.BOM_UTF_16BE) + xmlDeclEncoding = "UTF-16BE"; //$NON-NLS-1$ + else if (bom == IContentDescription.BOM_UTF_16LE) + xmlDeclEncoding = "UTF-16LE"; //$NON-NLS-1$ + // skip BOM to make comparison simpler + input.skip(bom.length); + // set the BOM in the description if requested + if (description != null && description.isRequested(IContentDescription.BYTE_ORDER_MARK)) + description.setProperty(IContentDescription.BYTE_ORDER_MARK, bom); + } + byte[] xmlPrefixBytes = XML_PREFIX.getBytes(xmlDeclEncoding); + byte[] prefix = new byte[xmlPrefixBytes.length]; + if (input.read(prefix) < prefix.length) + // there is not enough info to say anything + return INDETERMINATE; + for (int i = 0; i < prefix.length; i++) + if (prefix[i] != xmlPrefixBytes[i]) + // we don't have a XMLDecl... there is not enough info to say anything + return INDETERMINATE; + if (description == null) + return VALID; + // describe charset if requested + if (description.isRequested(IContentDescription.CHARSET)) { + String fullXMLDecl = readFullXMLDecl(input, xmlDeclEncoding); + if (fullXMLDecl != null) { + String charset = getCharset(fullXMLDecl); + if (charset != null && !"UTF-8".equalsIgnoreCase(charset)) //$NON-NLS-1$ + // only set property if value is not default (avoid using a non-default content description) + description.setProperty(IContentDescription.CHARSET, getCharset(fullXMLDecl)); + } + } + return VALID; + } + + private String readFullXMLDecl(InputStream input, String unicodeEncoding) throws IOException { + byte[] xmlDecl = new byte[100]; + int c = 0; + // looks for XMLDecl ending char (?) + int read = 0; + while (read < xmlDecl.length && (c = input.read()) != -1 && c != '?') + xmlDecl[read++] = (byte) c; + return c == '?' ? new String(xmlDecl, 0, read, unicodeEncoding) : null; + } + + public int describe(Reader input, IContentDescription description) throws IOException { + BufferedReader reader = new BufferedReader(input); + String line = reader.readLine(); + // end of stream + if (line == null) + return INDETERMINATE; + // XMLDecl should be the first string (no blanks allowed) + if (!line.startsWith(XML_PREFIX)) + return INDETERMINATE; + if (description == null) + return VALID; + // describe charset if requested + if ((description.isRequested(IContentDescription.CHARSET))) + description.setProperty(IContentDescription.CHARSET, getCharset(line)); + return VALID; + } + + private String getCharset(String firstLine) { + int encodingPos = firstLine.indexOf(ENCODING); + if (encodingPos == -1) + return null; + char quoteChar = '"'; + int firstQuote = firstLine.indexOf(quoteChar, encodingPos); + if (firstQuote == -1) { + quoteChar = '\''; + firstQuote = firstLine.indexOf(quoteChar, encodingPos); + } + if (firstQuote == -1 || firstLine.length() == firstQuote - 1) + return null; + int secondQuote = firstLine.indexOf(quoteChar, firstQuote + 1); + if (secondQuote == -1) + return null; + return firstLine.substring(firstQuote + 1, secondQuote); + } + + public QualifiedName[] getSupportedOptions() { + return SUPPORTED_OPTIONS; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/AbstractTransferListenerAdapter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/AbstractTransferListenerAdapter.java new file mode 100644 index 00000000..22d1f714 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/AbstractTransferListenerAdapter.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.osgi.util.NLS; + +import org.apache.maven.wagon.WagonConstants; + +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * AbstractTransferListenerAdapter + * + * @author igor + */ +abstract class AbstractTransferListenerAdapter { + + protected final MavenImpl maven; + + protected final IProgressMonitor monitor; + + protected final MavenConsole console; + + protected long complete = 0; + + private static final String[] units = {Messages.AbstractTransferListenerAdapter_byte, Messages.AbstractTransferListenerAdapter_kb, Messages.AbstractTransferListenerAdapter_mb}; + + protected AbstractTransferListenerAdapter(MavenImpl maven, IProgressMonitor monitor, MavenConsole console) { + this.maven = maven; + this.monitor = monitor == null ? new NullProgressMonitor() : monitor; + this.console = console; + } + + protected void formatBytes(long n, StringBuffer sb) { + int i = 0; + while(n >= 1024 && ++i < units.length) + n >>= 10; + + sb.append(n); + sb.append(units[i]); + } + + protected void transferInitiated(String artifactUrl) { + this.complete = 0; + + if (artifactUrl != null) { + monitor.subTask(artifactUrl); + } + } + + protected void transferStarted(String artifactUrl) { + console.logMessage(NLS.bind("Downloading {0}", artifactUrl)); + // monitor.beginTask("0% "+e.getWagon().getRepository()+"/"+e.getResource().getName(), IProgressMonitor.UNKNOWN); + monitor.subTask(Messages.AbstractTransferListenerAdapter_4 + artifactUrl); + } + + protected void transferProgress(String artifactUrl, long total, int length) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(Messages.AbstractTransferListenerAdapter_cancelled); + } + + complete += length; + + StringBuffer sb = new StringBuffer(); + + formatBytes(complete, sb); + if(total != WagonConstants.UNKNOWN_LENGTH) { + sb.append('/'); + formatBytes(total, sb); + if (total > 0) { + sb.append(" ("); + sb.append(100l * complete / total); + sb.append("%)"); + } + } + sb.append(' '); + + monitor.subTask(sb.toString() + artifactUrl); + } + + protected void transferCompleted(String artifactUrl) { + console.logMessage(NLS.bind("Downloaded {0}", artifactUrl)); + + // monitor.subTask("100% "+e.getWagon().getRepository()+"/"+e.getResource().getName()); + monitor.subTask(""); //$NON-NLS-1$ + } + + protected void transferError(String artifactUrl, Exception exception) { + console.logMessage(NLS.bind("Unable to download {0} : {1}", artifactUrl, exception)); + monitor.subTask(NLS.bind(Messages.AbstractTransferListenerAdapter_subtask, artifactUrl)); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ArtifactTransferListenerAdapter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ArtifactTransferListenerAdapter.java new file mode 100644 index 00000000..9562cb9c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ArtifactTransferListenerAdapter.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.sonatype.aether.transfer.TransferEvent; +import org.sonatype.aether.transfer.TransferListener; + +import org.eclipse.m2e.core.core.MavenConsole; + +/** + * ArtifactTransferListenerAdapter + * + * @author igor + */ +public class ArtifactTransferListenerAdapter extends AbstractTransferListenerAdapter implements + TransferListener { + + ArtifactTransferListenerAdapter(MavenImpl maven, IProgressMonitor monitor, MavenConsole console) { + super(maven, monitor, console); + } + + public void transferInitiated(TransferEvent event) { + transferInitiated(event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); + } + + public void transferProgressed(TransferEvent event) { + long total = event.getResource().getContentLength(); + String artifactUrl = event.getResource().getRepositoryUrl() + event.getResource().getResourceName(); + + transferProgress(artifactUrl, total, event.getDataBuffer().remaining()); + } + + public void transferStarted(TransferEvent event) { + transferStarted(event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); + } + + public void transferCorrupted(TransferEvent event) { + } + + public void transferSucceeded(TransferEvent event) { + transferCompleted(event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); + } + + public void transferFailed(TransferEvent event) { + transferCompleted(event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSession.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSession.java new file mode 100644 index 00000000..7ac41337 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSession.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.sonatype.aether.RepositorySystemSession; + +public interface ContextRepositorySystemSession extends RepositorySystemSession { + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSessionImpl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSessionImpl.java new file mode 100644 index 00000000..840e1938 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ContextRepositorySystemSessionImpl.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.util.Map; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +import org.apache.maven.plugin.LegacySupport; + +import org.sonatype.aether.RepositoryCache; +import org.sonatype.aether.RepositoryListener; +import org.sonatype.aether.RepositorySystemSession; +import org.sonatype.aether.SessionData; +import org.sonatype.aether.artifact.ArtifactTypeRegistry; +import org.sonatype.aether.collection.DependencyGraphTransformer; +import org.sonatype.aether.collection.DependencyManager; +import org.sonatype.aether.collection.DependencySelector; +import org.sonatype.aether.collection.DependencyTraverser; +import org.sonatype.aether.repository.AuthenticationSelector; +import org.sonatype.aether.repository.LocalRepository; +import org.sonatype.aether.repository.LocalRepositoryManager; +import org.sonatype.aether.repository.MirrorSelector; +import org.sonatype.aether.repository.ProxySelector; +import org.sonatype.aether.repository.WorkspaceReader; +import org.sonatype.aether.transfer.TransferListener; + + +@Component(role = ContextRepositorySystemSession.class) +public class ContextRepositorySystemSessionImpl implements ContextRepositorySystemSession { + + @Requirement + private LegacySupport context; + + private RepositorySystemSession getSession() { + RepositorySystemSession session = context.getRepositorySession(); + if(session == null) { + throw new IllegalStateException("no context maven session"); //$NON-NLS-1$ + } + return session; + } + + public ArtifactTypeRegistry getArtifactTypeRegistry() { + return getSession().getArtifactTypeRegistry(); + } + + public AuthenticationSelector getAuthenticationSelector() { + return getSession().getAuthenticationSelector(); + } + + public RepositoryCache getCache() { + return getSession().getCache(); + } + + public String getChecksumPolicy() { + return getSession().getChecksumPolicy(); + } + + public Map<String, Object> getConfigProperties() { + return getSession().getConfigProperties(); + } + + public SessionData getData() { + return getSession().getData(); + } + + public DependencyGraphTransformer getDependencyGraphTransformer() { + return getSession().getDependencyGraphTransformer(); + } + + public DependencyManager getDependencyManager() { + return getSession().getDependencyManager(); + } + + public DependencySelector getDependencySelector() { + return getSession().getDependencySelector(); + } + + public DependencyTraverser getDependencyTraverser() { + return getSession().getDependencyTraverser(); + } + + public LocalRepository getLocalRepository() { + return getSession().getLocalRepository(); + } + + public LocalRepositoryManager getLocalRepositoryManager() { + return getSession().getLocalRepositoryManager(); + } + + public MirrorSelector getMirrorSelector() { + return getSession().getMirrorSelector(); + } + + public ProxySelector getProxySelector() { + return getSession().getProxySelector(); + } + + public RepositoryListener getRepositoryListener() { + return getSession().getRepositoryListener(); + } + + public Map<String, String> getSystemProperties() { + return getSession().getSystemProperties(); + } + + public TransferListener getTransferListener() { + return getSession().getTransferListener(); + } + + public String getUpdatePolicy() { + return getSession().getUpdatePolicy(); + } + + public Map<String, String> getUserProperties() { + return getSession().getUserProperties(); + } + + public WorkspaceReader getWorkspaceReader() { + return getSession().getWorkspaceReader(); + } + + public boolean isIgnoreInvalidArtifactDescriptor() { + return getSession().isIgnoreInvalidArtifactDescriptor(); + } + + public boolean isIgnoreMissingArtifactDescriptor() { + return getSession().isIgnoreMissingArtifactDescriptor(); + } + + public boolean isNotFoundCachingEnabled() { + return getSession().isNotFoundCachingEnabled(); + } + + public boolean isOffline() { + return getSession().isOffline(); + } + + public boolean isTransferErrorCachingEnabled() { + return getSession().isTransferErrorCachingEnabled(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/DefaultMavenComponentContributor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/DefaultMavenComponentContributor.java new file mode 100644 index 00000000..038d1452 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/DefaultMavenComponentContributor.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.apache.maven.classrealm.ClassRealmManagerDelegate; +import org.apache.maven.plugin.internal.PluginDependenciesResolver; +import org.apache.maven.project.artifact.MavenMetadataCache; + +import org.sonatype.aether.impl.LocalRepositoryMaintainer; +import org.sonatype.plexus.build.incremental.BuildContext; + +import org.eclipse.m2e.core.internal.project.EclipseMavenMetadataCache; +import org.eclipse.m2e.core.internal.project.registry.EclipsePluginDependenciesResolver; + + +/** + */ +public class DefaultMavenComponentContributor implements IMavenComponentContributor { + + public void contribute(IMavenComponentBinder binder) { + binder.bind(MavenMetadataCache.class, EclipseMavenMetadataCache.class, null); + binder.bind(PluginDependenciesResolver.class, EclipsePluginDependenciesResolver.class, null); + binder.bind(BuildContext.class, EclipseBuildContext.class, null); + binder.bind(ClassRealmManagerDelegate.class, EclipseClassRealmManagerDelegate.class, EclipseClassRealmManagerDelegate.ROLE_HINT); + binder.bind(LocalRepositoryMaintainer.class, EclipseLocalRepositoryMaintainer.class, EclipseLocalRepositoryMaintainer.ROLE_HINT); + binder.bind(ContextRepositorySystemSession.class, ContextRepositorySystemSessionImpl.class, null); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseBuildContext.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseBuildContext.java new file mode 100644 index 00000000..52cfc528 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseBuildContext.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.codehaus.plexus.component.annotations.Component; + +import org.sonatype.plexus.build.incremental.BuildContext; +import org.sonatype.plexus.build.incremental.ThreadBuildContext; + + +/** + * Incremental build context. + */ +@Component(role = BuildContext.class) +public class EclipseBuildContext extends ThreadBuildContext { +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseClassRealmManagerDelegate.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseClassRealmManagerDelegate.java new file mode 100644 index 00000000..b623d1ec --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseClassRealmManagerDelegate.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.classrealm.ClassRealmConstituent; +import org.apache.maven.classrealm.ClassRealmManagerDelegate; +import org.apache.maven.classrealm.ClassRealmRequest; + + +/** + * EclipseArtifactFilterManager + * + * @author igor + */ +@Component(role = ClassRealmManagerDelegate.class) +public class EclipseClassRealmManagerDelegate implements ClassRealmManagerDelegate { + + public static final String ROLE_HINT = EclipseClassRealmManagerDelegate.class.getName(); + + @Requirement + private PlexusContainer plexus; + + private final ArtifactVersion currentBuildApiVersion; + + public EclipseClassRealmManagerDelegate() { + Properties props = new Properties(); + InputStream is = getClass().getResourceAsStream("/org/sonatype/plexus/build/incremental/version.properties"); //$NON-NLS-1$ + if(is != null) { + try { + props.load(is); + } catch(IOException e) { + e.printStackTrace(); + } + } + currentBuildApiVersion = new DefaultArtifactVersion(props.getProperty("api.version", "0.0.5")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void setupRealm(ClassRealm realm, ClassRealmRequest request) { + if(supportsBuildApi(request.getConstituents())) { + ClassRealm coreRealm = plexus.getContainerRealm(); + + realm.importFrom(coreRealm, "org.codehaus.plexus.util.AbstractScanner"); //$NON-NLS-1$ + realm.importFrom(coreRealm, "org.codehaus.plexus.util.Scanner"); //$NON-NLS-1$ + + realm.importFrom(coreRealm, "org.sonatype.plexus.build.incremental"); //$NON-NLS-1$ + } + } + + private boolean supportsBuildApi(List<ClassRealmConstituent> constituents) { + for(Iterator<ClassRealmConstituent> it = constituents.iterator(); it.hasNext();) { + ClassRealmConstituent constituent = it.next(); + if("org.sonatype.plexus".equals(constituent.getGroupId()) //$NON-NLS-1$ + && "plexus-build-api".equals(constituent.getArtifactId())) { //$NON-NLS-1$ + ArtifactVersion version = new DefaultArtifactVersion(constituent.getVersion()); + boolean compatible = currentBuildApiVersion.compareTo(version) >= 0; + if(compatible) { + // removing the JAR from the plugin realm to prevent discovery of the DefaultBuildContext + it.remove(); + } + return compatible; + } + } + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLocalRepositoryMaintainer.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLocalRepositoryMaintainer.java new file mode 100644 index 00000000..77ce63c7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLocalRepositoryMaintainer.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.File; + +import org.codehaus.plexus.component.annotations.Component; + +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.impl.LocalRepositoryEvent; +import org.sonatype.aether.impl.LocalRepositoryMaintainer; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ILocalRepositoryListener; + +/** + * EclipseLocalRepositoryMaintainer + * + * @author igor + */ +@Component(role = LocalRepositoryMaintainer.class) +public class EclipseLocalRepositoryMaintainer implements LocalRepositoryMaintainer { + + public static final String ROLE_HINT = EclipseLocalRepositoryMaintainer.class.getName(); + + public void artifactDownloaded(LocalRepositoryEvent event) { + notifyListeners(event); + } + + public void artifactInstalled(LocalRepositoryEvent event) { + notifyListeners(event); + } + + private void notifyListeners(LocalRepositoryEvent event) { + MavenImpl maven = (MavenImpl) MavenPlugin.getDefault().getMaven(); + + File basedir = event.getRepository().getBasedir(); + Artifact artifact = event.getArtifact(); + ArtifactKey key = new ArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), + artifact.getClassifier()); + for(ILocalRepositoryListener listener : maven.getLocalRepositoryListeners()) { + listener.artifactInstalled(basedir, key, event.getFile()); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLogger.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLogger.java new file mode 100644 index 00000000..16cbe8b6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLogger.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.logging.Logger; + +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.Messages; + +class EclipseLogger implements Logger { + private MavenConsole console; + private final IMavenConfiguration mavenConfiguration; + + public EclipseLogger(MavenConsole console, IMavenConfiguration mavenConfiguration) { + this.console = console; + this.mavenConfiguration = mavenConfiguration; + } + + private void out(String s) { + console.logMessage(s); + } + + private void outError(String s) { + console.logError(s); + } + + public void debug( String msg ) { + if (isDebugEnabled()) { + out(NLS.bind(Messages.EclipseLogger_debug1,msg)); + } + } + + public void debug( String msg, Throwable t) { + if (isDebugEnabled()) { + out( NLS.bind(Messages.EclipseLogger_debug2, msg, t.getMessage())); + } + } + + public void info( String msg ) { + if (isInfoEnabled()) { + out( NLS.bind(Messages.EclipseLogger_info1, msg)); + } + } + + public void info( String msg, Throwable t ) { + if (isInfoEnabled()) { + out( NLS.bind(Messages.EclipseLogger_info2, msg, t.getMessage())); + } + } + + public void warn( String msg ) { + if (isWarnEnabled()) { + out(NLS.bind(Messages.EclipseLogger_warn1, msg)); + } + } + + public void warn( String msg, Throwable t ) { + if (isWarnEnabled()) { + out( NLS.bind(Messages.EclipseLogger_warn2, msg, t.getMessage())); + } + } + + public void fatalError( String msg ) { + if (isFatalErrorEnabled()) { + outError(NLS.bind(Messages.EclipseLogger_fatal1, msg)); + } + } + + public void fatalError( String msg, Throwable t ) { + if (isFatalErrorEnabled()) { + outError( NLS.bind(Messages.EclipseLogger_fatal2, msg, t.getMessage())); + } + } + + public void error( String msg ) { + if (isErrorEnabled()) { + outError(NLS.bind(Messages.EclipseLogger_error1, msg)); + } + } + + public void error( String msg, Throwable t ) { + if (isErrorEnabled()) { + outError( NLS.bind(Messages.EclipseLogger_error2, msg, t.getMessage())); + } + } + + public boolean isDebugEnabled() { + return mavenConfiguration.isDebugOutput(); + } + + public boolean isInfoEnabled() { + return true; + } + + public boolean isWarnEnabled() { + return true; + } + + public boolean isErrorEnabled() { + return true; + } + + public boolean isFatalErrorEnabled() { + return true; + } + + public void setThreshold( int treshold ) { + } + + public int getThreshold() { + return LEVEL_DEBUG; + } + + public Logger getChildLogger(String name) { + return this; + } + + public String getName() { + return Messages.EclipseLogger_name; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLoggerManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLoggerManager.java new file mode 100644 index 00000000..2e6c4dd4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/EclipseLoggerManager.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.codehaus.plexus.logging.AbstractLoggerManager; +import org.codehaus.plexus.logging.Logger; + +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; + + +/** + * EclipseLoggerManager + * + * @author igor + */ +public class EclipseLoggerManager extends AbstractLoggerManager { + + private EclipseLogger logger; + + public EclipseLoggerManager(MavenConsole console, IMavenConfiguration mavenConfiguration) { + this.logger = new EclipseLogger(console, mavenConfiguration); + } + + public int getActiveLoggerCount() { + return 1; + } + + public Logger getLoggerForComponent(String arg0, String arg1) { + return logger; + } + + public int getThreshold() { + return Logger.LEVEL_DEBUG; + } + + public int getThreshold(String arg0, String arg1) { + return Logger.LEVEL_DEBUG; + } + + public void returnComponentLogger(String arg0, String arg1) { + } + + public void setThreshold(int arg0) { + } + + public void setThreshold(String arg0, String arg1, int arg2) { + } + + public void setThresholds(int arg0) { + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ExtensionModule.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ExtensionModule.java new file mode 100644 index 00000000..ddf309c1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/ExtensionModule.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import com.google.inject.AbstractModule; +import com.google.inject.name.Names; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * A custom Guice module that picks the components contributed by extensions. + */ +class ExtensionModule extends AbstractModule implements IMavenComponentContributor.IMavenComponentBinder { + + public <T> void bind(Class<T> role, Class<? extends T> impl, String hint) { + if(hint == null || hint.length() <= 0 || "default".equals(hint)) { //$NON-NLS-1$ + bind(role).to(impl); + } else { + bind(role).annotatedWith(Names.named(hint)).to(impl); + } + } + + protected void configure() { + IExtensionRegistry r = Platform.getExtensionRegistry(); + for(IConfigurationElement c : r.getConfigurationElementsFor(IMavenConstants.MAVEN_COMPONENT_CONTRIBUTORS_XPT)) { + if("configurator".equals(c.getName())) { //$NON-NLS-1$ + try { + IMavenComponentContributor contributor = (IMavenComponentContributor) c.createExecutableExtension("class"); //$NON-NLS-1$ + contributor.contribute(this); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/IMavenComponentContributor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/IMavenComponentContributor.java new file mode 100644 index 00000000..fd4963cc --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/IMavenComponentContributor.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +/** + * Allows extensions to contribute components to the Maven core container. + */ +public interface IMavenComponentContributor { + + void contribute(IMavenComponentBinder binder); + + public interface IMavenComponentBinder { + + <T> void bind(Class<T> role, Class<? extends T> impl, String hint); + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenConfigurationImpl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenConfigurationImpl.java new file mode 100644 index 00000000..fd786884 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenConfigurationImpl.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.IMavenConfigurationChangeListener; +import org.eclipse.m2e.core.embedder.MavenConfigurationChangeEvent; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +public class MavenConfigurationImpl implements IMavenConfiguration, IPropertyChangeListener { + + private final IPreferenceStore preferenceStore; + private final ListenerList listeners = new ListenerList(ListenerList.IDENTITY); + + public MavenConfigurationImpl(IPreferenceStore preferenceStore) { + this.preferenceStore = preferenceStore; + preferenceStore.addPropertyChangeListener(this); + } + + public String getGlobalSettingsFile() { + return preferenceStore.getString(MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE); + } + + public String getJiraPassword() { + return preferenceStore.getString(MavenPreferenceConstants.P_JIRA_PASSWORD); + } + + public String getJiraUsername() { + return preferenceStore.getString(MavenPreferenceConstants.P_JIRA_USERNAME); + } + + public String getUserSettingsFile() { + return preferenceStore.getString(MavenPreferenceConstants.P_USER_SETTINGS_FILE); + } + + public boolean isDebugOutput() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_DEBUG_OUTPUT); + } + + public boolean isDownloadJavaDoc() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_DOWNLOAD_JAVADOC); + } + + public boolean isDownloadSources() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_DOWNLOAD_SOURCES); + } + + public boolean isHideFoldersOfNestedProjects() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_HIDE_FOLDERS_OF_NESTED_PROJECTS); + } + + public boolean isOffline() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_OFFLINE); + } + + public void setUserSettingsFile(String settingsFile) { + preferenceStore.setValue(MavenPreferenceConstants.P_USER_SETTINGS_FILE, nvl(settingsFile)); + } + + public void setGlobalSettingsFile(String globalSettingsFile){ + preferenceStore.setValue(MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE, nvl(globalSettingsFile)); + } + + private static String nvl(String s) { + return s == null ? "" : s; //$NON-NLS-1$ + } + + public boolean isUpdateProjectsOnStartup() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_UPDATE_PROJECTS); + } + + public boolean isUpdateIndexesOnStartup() { + return preferenceStore.getBoolean(MavenPreferenceConstants.P_UPDATE_INDEXES); + } + + public synchronized void addConfigurationChangeListener(IMavenConfigurationChangeListener listener) { + this.listeners.add(listener); + } + + public void propertyChange(PropertyChangeEvent event) { + MavenConfigurationChangeEvent mavenEvent = new MavenConfigurationChangeEvent(event.getProperty(), event.getNewValue(), event.getOldValue()); + for (Object listener : listeners.getListeners()) { + try { + ((IMavenConfigurationChangeListener) listener).mavenConfigutationChange(mavenEvent); + } catch (Exception e) { + MavenLogger.log("Could not deliver maven configuration change event", e); + } + } + + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenEmbeddedRuntime.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenEmbeddedRuntime.java new file mode 100644 index 00000000..e9142b62 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenEmbeddedRuntime.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMavenLauncherConfiguration; +import org.eclipse.m2e.core.embedder.MavenRuntime; +import org.eclipse.m2e.core.embedder.MavenRuntimeManager; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Embedded Maven runtime + * + * @author Eugene Kuleshov + * @author Igor Fedorenko + */ +public class MavenEmbeddedRuntime implements MavenRuntime { + + private static final String MAVEN_MAVEN_EMBEDDER_BUNDLE_ID = "org.eclipse.m2e.maven.runtime"; //$NON-NLS-1$ + + private static final String MAVEN_EXECUTOR_CLASS = org.apache.maven.cli.MavenCli.class.getName(); + + public static final String PLEXUS_CLASSWORLD_NAME = "plexus.core"; //$NON-NLS-1$ + + private static String[] LAUNCHER_CLASSPATH; + private static String[] CLASSPATH; + + private static volatile String mavenVersion; + + private BundleContext bundleContext; + + public MavenEmbeddedRuntime(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public boolean isEditable() { + return false; + } + + public String getLocation() { + return MavenRuntimeManager.EMBEDDED; + } + + public String getSettings() { + return null; + } + + public boolean isAvailable() { + return true; + } + + public void createLauncherConfiguration(IMavenLauncherConfiguration collector, IProgressMonitor monitor) throws CoreException { + collector.setMainType(MAVEN_EXECUTOR_CLASS, PLEXUS_CLASSWORLD_NAME); + + initClasspath(findMavenEmbedderBundle()); + + collector.addRealm(IMavenLauncherConfiguration.LAUNCHER_REALM); + for(String entry : LAUNCHER_CLASSPATH) { + collector.addArchiveEntry(entry); + } + + collector.addRealm(PLEXUS_CLASSWORLD_NAME); + for(String entry : CLASSPATH) { + // https://issues.sonatype.org/browse/MNGECLIPSE-2507 + if(!entry.contains("plexus-build-api")) { + collector.addArchiveEntry(entry); + } + } + } + + private static synchronized void initClasspath(Bundle bundle) { + if(CLASSPATH == null) { + List<String> cp = new ArrayList<String>(); + List<String> lcp = new ArrayList<String>(); + + @SuppressWarnings("unchecked") + Enumeration<URL> entries = bundle.findEntries("/", "*", true); //$NON-NLS-1$ //$NON-NLS-2$ + while(entries.hasMoreElements()) { + URL url = entries.nextElement(); + String path = url.getPath(); + if(path.endsWith(".jar") || path.endsWith("bin/")) { //$NON-NLS-1$ //$NON-NLS-2$ + try { + String file = FileLocator.toFileURL(url).getFile(); + if (file.contains("plexus-classworlds")) { //$NON-NLS-1$ + lcp.add(file); + } else { + cp.add(file); + } + } catch(IOException ex) { + MavenLogger.log("Error adding classpath entry " + url.toString(), ex); + } + } + } + + CLASSPATH = cp.toArray(new String[cp.size()]); + LAUNCHER_CLASSPATH = lcp.toArray(new String[lcp.size()]); + } + } + + private Bundle findMavenEmbedderBundle() { + Bundle bundle = null; + Bundle[] bundles = bundleContext.getBundles(); + for(int i = 0; i < bundles.length; i++ ) { + if(MAVEN_MAVEN_EMBEDDER_BUNDLE_ID.equals(bundles[i].getSymbolicName())) { + bundle = bundles[i]; + break; + } + } + return bundle; + } + + public String toString() { + Bundle embedder = Platform.getBundle(MAVEN_MAVEN_EMBEDDER_BUNDLE_ID); + + StringBuilder sb = new StringBuilder(); + sb.append("Embedded (").append(getVersion()); //$NON-NLS-1$ + if (embedder != null) { + String version = (String) embedder.getHeaders().get(Constants.BUNDLE_VERSION); + sb.append('/').append(version); + } + sb.append(')'); + + return sb.toString(); + } + + private static synchronized String getVersion(Bundle bundle) { + if(mavenVersion != null) { + return mavenVersion; + } + initClasspath(bundle); + try { + String mavenCoreJarPath = null; + for(String path : CLASSPATH) { + if(path.contains("maven-core") && path.endsWith(".jar")) { + mavenCoreJarPath = path; + break; + } + } + + if(mavenCoreJarPath == null) { + throw new RuntimeException("Could not find maven core jar file"); + } + + ZipFile zip = new ZipFile(mavenCoreJarPath); + try { + ZipEntry zipEntry = zip.getEntry("META-INF/maven/org.apache.maven/maven-core/pom.properties"); //$NON-NLS-1$ + if(zipEntry != null) { + Properties pomProperties = new Properties(); + pomProperties.load(zip.getInputStream(zipEntry)); + + String version = pomProperties.getProperty("version"); //$NON-NLS-1$ + if(version != null) { + mavenVersion = version; + return mavenVersion; + } + } + } finally { + zip.close(); + } + } catch(Exception e) { + MavenLogger.log("Could not determine embedded maven version", e); + } + + return Messages.MavenEmbeddedRuntime_unknown; + } + + public String getVersion() { + Bundle bundle = findMavenEmbedderBundle(); + return getVersion(bundle); + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenExternalRuntime.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenExternalRuntime.java new file mode 100644 index 00000000..e11f84b1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenExternalRuntime.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import org.codehaus.plexus.classworlds.launcher.ConfigurationHandler; +import org.codehaus.plexus.classworlds.launcher.ConfigurationParser; +import org.codehaus.plexus.util.DirectoryScanner; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMavenLauncherConfiguration; +import org.eclipse.m2e.core.embedder.MavenRuntime; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Maven external runtime using ClassWorlds launcher + * + * @author Eugene Kuleshov + * @author Igor Fedorenko + * + */ +public class MavenExternalRuntime implements MavenRuntime { + + private static final String PROPERTY_MAVEN_HOME = "maven.home"; //$NON-NLS-1$ + + private final String location; + + + public MavenExternalRuntime(String location) { + this.location = location; + } + + public boolean isEditable() { + return true; + } + + public boolean isAvailable() { + return new File(location, "bin").exists() && getLauncherClasspath() != null; //$NON-NLS-1$ + } + + public String getLocation() { + return location; + } + + public String getSettings() { + return location + File.separator + "conf" + File.separator + "settings.xml"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public String getMainTypeName() { + return "org.codehaus.classworlds.Launcher"; //$NON-NLS-1$ + } + + private File getLauncherConfigurationFile() { + return new File(location, "bin/m2.conf"); //$NON-NLS-1$ + } + + public void createLauncherConfiguration(final IMavenLauncherConfiguration collector, IProgressMonitor monitor) throws CoreException { + + collector.addRealm(IMavenLauncherConfiguration.LAUNCHER_REALM); + collector.addArchiveEntry(getLauncherClasspath()); + + ConfigurationHandler handler = new ConfigurationHandler() { + public void addImportFrom(String relamName, String importSpec) { + throw new UnsupportedOperationException(Messages.MavenExternalRuntime_exc_unsupported); + } + public void addLoadFile(File file) { + try { + collector.addArchiveEntry(file.getAbsolutePath()); + } catch(CoreException ex) { + throw new ExceptionWrapper(ex); + } + } + public void addLoadURL(URL url) { + try { + collector.addArchiveEntry(url.toExternalForm()); + } catch(CoreException ex) { + throw new ExceptionWrapper(ex); + } + } + public void addRealm(String realmName) { + collector.addRealm(realmName); + } + public void setAppMain(String mainClassName, String mainRealmName) { + collector.setMainType(mainClassName, mainRealmName); + } + }; + + Properties properties = new Properties(); + properties.put(PROPERTY_MAVEN_HOME, location); + + ConfigurationParser parser = new ConfigurationParser(handler, properties); + + try { + FileInputStream is = new FileInputStream(getLauncherConfigurationFile()); + try { + parser.parse(is); + } finally { + is.close(); + } + } catch (Exception e) { + if (e instanceof ExceptionWrapper && e.getCause() instanceof CoreException) { + throw (CoreException) e.getCause(); + } + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenExternalRuntime_error_cannot_parse, e)); + } + + // XXX show error dialog and fail launch + } + + public boolean equals(Object o) { + if(o instanceof MavenExternalRuntime) { + return location.equals(((MavenExternalRuntime) o).location); + } + return false; + } + + public int hashCode() { + return location.hashCode(); + } + + public String toString() { + return "External" + " " + location + " (" + getVersion() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + private static class ExceptionWrapper extends RuntimeException { + private static final long serialVersionUID = 8815818826909815028L; + public ExceptionWrapper(Exception cause) { + super(cause); + } + } + + private String getLauncherClasspath() { + File mavenHome = new File(location); + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir(mavenHome); + ds.setIncludes(new String[] { + "core/boot/classworlds*.jar", // 2.0.4 //$NON-NLS-1$ + "boot/classworlds*.jar", // 2.0.7 //$NON-NLS-1$ + "boot/plexus-classworlds*.jar", // 2.1 as of 2008-03-27 //$NON-NLS-1$ + }); + ds.scan(); + String[] includedFiles = ds.getIncludedFiles(); + + if (includedFiles.length == 1) { + return new File(mavenHome, includedFiles[0]).getAbsolutePath(); + } + + return null; + } + + public String getVersion() { + + class VersionHandler implements ConfigurationHandler { + File mavenCore; + File uber; + public void addImportFrom(String relamName, String importSpec) { + } + public void addLoadFile(File file) { + if (file.getName().contains("maven-core")) { //$NON-NLS-1$ + mavenCore = file; + } else if (file.getName().endsWith("uber.jar")) { //$NON-NLS-1$ + uber = file; + } + } + public void addLoadURL(URL url) { + } + public void addRealm(String realmName) { + } + public void setAppMain(String mainClassName, String mainRealmName) { + } + }; + VersionHandler handler = new VersionHandler(); + + Properties properties = new Properties(); + properties.put(PROPERTY_MAVEN_HOME, location); + + ConfigurationParser parser = new ConfigurationParser(handler, properties); + + try { + FileInputStream is = new FileInputStream(getLauncherConfigurationFile()); + try { + parser.parse(is); + } finally { + is.close(); + } + + ZipFile zip = null; + if (handler.mavenCore != null) { + zip = new ZipFile(handler.mavenCore); + } else if (handler.uber != null) { + zip = new ZipFile(handler.uber); + } + if (zip != null) { + try { + ZipEntry zipEntry = zip.getEntry("META-INF/maven/org.apache.maven/maven-core/pom.properties"); //$NON-NLS-1$ + if (zipEntry != null) { + Properties pomProperties = new Properties(); + pomProperties.load(zip.getInputStream(zipEntry)); + + String version = pomProperties.getProperty("version"); //$NON-NLS-1$ + if (version != null) { + return version; + } + } + } finally { + zip.close(); + } + } + + } catch (Exception e) { + // most likely a bad location, but who knows + MavenLogger.log("Could not parse classwords configuration file", e); + } + + + return Messages.MavenExternalRuntime_unknown; + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java new file mode 100644 index 00000000..6d4fb2a4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenImpl.java @@ -0,0 +1,1098 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.ContainerConfiguration; +import org.codehaus.plexus.DefaultContainerConfiguration; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.MutablePlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.PlexusContainerException; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; +import org.codehaus.plexus.component.configurator.ComponentConfigurationException; +import org.codehaus.plexus.component.configurator.converters.ConfigurationConverter; +import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup; +import org.codehaus.plexus.component.configurator.converters.lookup.DefaultConverterLookup; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.PlexusConfiguration; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import org.apache.maven.DefaultMaven; +import org.apache.maven.Maven; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.InvalidRepositoryException; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionRequestPopulationException; +import org.apache.maven.execution.MavenExecutionRequestPopulator; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.LifecycleExecutor; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.model.ConfigurationContainer; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Profile; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.io.ModelReader; +import org.apache.maven.model.io.ModelWriter; +import org.apache.maven.plugin.BuildPluginManager; +import org.apache.maven.plugin.InvalidPluginDescriptorException; +import org.apache.maven.plugin.MavenPluginManager; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoNotFoundException; +import org.apache.maven.plugin.PluginConfigurationException; +import org.apache.maven.plugin.PluginContainerException; +import org.apache.maven.plugin.PluginDescriptorParsingException; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginNotFoundException; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.version.DefaultPluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionRequest; +import org.apache.maven.plugin.version.PluginVersionResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolver; +import org.apache.maven.project.DuplicateProjectException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.project.ProjectSorter; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.repository.internal.MavenRepositorySystemSession; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Proxy; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.SettingsUtils; +import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; +import org.apache.maven.settings.building.DefaultSettingsProblem; +import org.apache.maven.settings.building.SettingsBuilder; +import org.apache.maven.settings.building.SettingsBuildingException; +import org.apache.maven.settings.building.SettingsBuildingRequest; +import org.apache.maven.settings.building.SettingsProblem; +import org.apache.maven.settings.building.SettingsProblem.Severity; +import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.apache.maven.settings.io.SettingsWriter; +import org.apache.maven.wagon.proxy.ProxyInfo; + +import org.sonatype.aether.RepositorySystemSession; +import org.sonatype.aether.repository.LocalRepository; +import org.sonatype.aether.resolution.ArtifactRequest; +import org.sonatype.aether.resolution.ArtifactResolutionException; +import org.sonatype.aether.resolution.ArtifactResult; +import org.sonatype.aether.transfer.ArtifactNotFoundException; +import org.sonatype.aether.transfer.TransferListener; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ILocalRepositoryListener; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.IMavenConfigurationChangeListener; +import org.eclipse.m2e.core.embedder.ISettingsChangeListener; +import org.eclipse.m2e.core.embedder.MavenConfigurationChangeEvent; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +public class MavenImpl implements IMaven, IMavenConfigurationChangeListener { + + /** + * Id of maven core class realm + */ + public static final String MAVEN_CORE_REALM_ID = "plexus.core"; //$NON-NLS-1$ + + + private DefaultPlexusContainer plexus; + + private final IMavenConfiguration mavenConfiguration; + + private final ConverterLookup converterLookup = new DefaultConverterLookup(); + + private final MavenConsole console; + + private final ArrayList<ISettingsChangeListener> settingsListeners = new ArrayList<ISettingsChangeListener>(); + + private final ArrayList<ILocalRepositoryListener> localRepositoryListeners = new ArrayList<ILocalRepositoryListener>(); + + public MavenImpl(IMavenConfiguration mavenConfiguration, MavenConsole console) { + this.console = console; + this.mavenConfiguration = mavenConfiguration; + mavenConfiguration.addConfigurationChangeListener(this); + } + + public MavenExecutionRequest createExecutionRequest(IProgressMonitor monitor) throws CoreException { + MavenExecutionRequest request = new DefaultMavenExecutionRequest(); + if(mavenConfiguration.getGlobalSettingsFile() != null) { + request.setGlobalSettingsFile(new File(mavenConfiguration.getGlobalSettingsFile())); + } + if(mavenConfiguration.getUserSettingsFile() != null) { + request.setUserSettingsFile(new File(mavenConfiguration.getUserSettingsFile())); + } + + try { + lookup(MavenExecutionRequestPopulator.class).populateFromSettings(request, getSettings()); + } catch(MavenExecutionRequestPopulationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_no_exec_req, ex)); + } + + ArtifactRepository localRepository = getLocalRepository(); + request.setLocalRepository(localRepository); + request.setLocalRepositoryPath(localRepository.getBasedir()); + request.setOffline(mavenConfiguration.isOffline()); + + // logging + request.setTransferListener(createArtifactTransferListener(monitor)); + + request.getUserProperties().put("m2e.version", MavenPlugin.getVersion()); //$NON-NLS-1$ + + request.setCacheNotFound(true); + request.setCacheTransferError(true); + + // the right way to disable snapshot update + // request.setUpdateSnapshots(false); + return request; + } + + public String getLocalRepositoryPath() { + String path = null; + try { + Settings settings = getSettings(); + path = settings.getLocalRepository(); + } catch(CoreException ex) { + // fall through + } + if(path == null) { + path = RepositorySystem.defaultUserLocalRepository.getAbsolutePath(); + } + return path; + } + + public MavenExecutionResult execute(MavenExecutionRequest request, IProgressMonitor monitor) { + // XXX is there a way to set per-request log level? + + MavenExecutionResult result; + try { + lookup(MavenExecutionRequestPopulator.class).populateDefaults(request); + result = lookup(Maven.class).execute(request); + } catch(MavenExecutionRequestPopulationException ex) { + result = new DefaultMavenExecutionResult(); + result.addException(ex); + } catch(Exception e) { + result = new DefaultMavenExecutionResult(); + result.addException(e); + } + return result; + } + + public MavenSession createSession(MavenExecutionRequest request, MavenProject project) { + RepositorySystemSession repoSession = createRepositorySession(request); + MavenExecutionResult result = new DefaultMavenExecutionResult(); + MavenSession mavenSession = new MavenSession(plexus, repoSession, request, result); + if(project != null) { + mavenSession.setProjects(Collections.singletonList(project)); + } + return mavenSession; + } + + private RepositorySystemSession createRepositorySession(MavenExecutionRequest request) { + try { + return ((DefaultMaven) lookup(Maven.class)).newRepositorySession(request); + } catch(CoreException ex) { + MavenLogger.log(ex); + throw new IllegalStateException("Could not look up Maven embedder", ex); + } + } + + public void execute(MavenSession session, MojoExecution execution, IProgressMonitor monitor) { + try { + lookup(BuildPluginManager.class).executeMojo(session, execution); + } catch(Exception ex) { + session.getResult().addException(ex); + } + } + + public <T> T getConfiguredMojo(MavenSession session, MojoExecution mojoExecution, Class<T> clazz) + throws CoreException { + try { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + // getPluginRealm creates plugin realm and populates pluginDescriptor.classRealm field + lookup(BuildPluginManager.class).getPluginRealm(session, mojoDescriptor.getPluginDescriptor()); + return clazz.cast(lookup(MavenPluginManager.class).getConfiguredMojo(Mojo.class, session, mojoExecution)); + } catch(PluginContainerException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenImpl_error_mojo, mojoExecution), ex)); + } catch(PluginConfigurationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenImpl_error_mojo, mojoExecution), ex)); + } catch(ClassCastException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenImpl_error_mojo, mojoExecution), ex)); + } catch(PluginResolutionException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenImpl_error_mojo, mojoExecution), ex)); + } catch(PluginManagerException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenImpl_error_mojo, mojoExecution), ex)); + } + } + + public void releaseMojo(Object mojo, MojoExecution mojoExecution) throws CoreException { + lookup(MavenPluginManager.class).releaseMojo(mojo, mojoExecution); + } + + public MavenExecutionPlan calculateExecutionPlan(MavenExecutionRequest request, MavenProject project, + IProgressMonitor monitor) throws CoreException { + MavenSession session = createSession(request, project); + try { + List<String> goals = request.getGoals(); + return lookup(LifecycleExecutor.class).calculateExecutionPlan(session, goals.toArray(new String[goals.size()])); + } catch(Exception ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_calc_build_plan, ex)); + } + } + + public ArtifactRepository getLocalRepository() throws CoreException { + try { + String localRepositoryPath = getLocalRepositoryPath(); + if(localRepositoryPath != null) { + return lookup(RepositorySystem.class).createLocalRepository(new File(localRepositoryPath)); + } + return lookup(RepositorySystem.class).createLocalRepository(RepositorySystem.defaultUserLocalRepository); + } catch(InvalidRepositoryException ex) { + // can't happen + throw new IllegalStateException(ex); + } + } + + public Settings getSettings() throws CoreException { + // MUST NOT use createRequest! + + // TODO: Can't that delegate to buildSettings()? + SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); + request.setSystemProperties(System.getProperties()); + if(mavenConfiguration.getGlobalSettingsFile() != null) { + request.setGlobalSettingsFile(new File(mavenConfiguration.getGlobalSettingsFile())); + } + if(mavenConfiguration.getUserSettingsFile() != null) { + request.setUserSettingsFile(new File(mavenConfiguration.getUserSettingsFile())); + } + try { + return lookup(SettingsBuilder.class).build(request).getEffectiveSettings(); + } catch(SettingsBuildingException ex) { + String msg = "Could not read settings.xml, assuming default values"; + MavenPlugin.getDefault().getConsole().logError(msg); + MavenLogger.log(msg, ex); + /* + * NOTE: This method provides input for various other core functions, just bailing out would make m2e highly + * unusuable. Instead, we fail gracefully and just ignore the broken settings, using defaults. + */ + return new Settings(); + } + } + + public Settings buildSettings(String globalSettings, String userSettings) throws CoreException { + SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); + request.setGlobalSettingsFile(globalSettings != null ? new File(globalSettings) : null); + request.setUserSettingsFile(userSettings != null ? new File(userSettings) : null); + try { + return lookup(SettingsBuilder.class).build(request).getEffectiveSettings(); + } catch(SettingsBuildingException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_settings, + ex)); + } + } + + public void writeSettings(Settings settings, OutputStream out) throws CoreException { + try { + lookup(SettingsWriter.class).write(out, null, settings); + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_write_settings, + ex)); + } + } + + public List<SettingsProblem> validateSettings(String settings) { + List<SettingsProblem> problems = new ArrayList<SettingsProblem>(); + if(settings != null) { + File settingsFile = new File(settings); + if(settingsFile.canRead()) { + SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); + request.setUserSettingsFile(settingsFile); + try { + lookup(SettingsBuilder.class).build(request); + } catch(SettingsBuildingException ex) { + problems.addAll(ex.getProblems()); + } catch(CoreException ex) { + problems.add(new DefaultSettingsProblem(ex.getMessage(), Severity.FATAL, settings, -1, -1, ex)); + } + } else { + problems.add(new DefaultSettingsProblem(NLS.bind(Messages.MavenImpl_error_read_settings2, settings), + SettingsProblem.Severity.ERROR, settings, -1, -1, null)); + } + } + + return problems; + } + + public void reloadSettings() throws CoreException { + // TODO do something more meaningful + Settings settings = getSettings(); + for(ISettingsChangeListener listener : settingsListeners) { + try { + listener.settingsChanged(settings); + } catch(CoreException e) { + MavenLogger.log(e); + } + } + } + + public Server decryptPassword(Server server) throws CoreException { + SettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(server); + SettingsDecryptionResult result = lookup(SettingsDecrypter.class).decrypt(request); + for(SettingsProblem problem : result.getProblems()) { + MavenLogger.log(new Status(IStatus.WARNING, IMavenConstants.PLUGIN_ID, -1, problem.getMessage(), problem + .getException())); + } + return result.getServer(); + } + + public void mavenConfigutationChange(MavenConfigurationChangeEvent event) throws CoreException { + if(MavenConfigurationChangeEvent.P_USER_SETTINGS_FILE.equals(event.getKey()) + || MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE.equals(event.getKey())) { + reloadSettings(); + } + } + + public Model readModel(InputStream in) throws CoreException { + try { + return lookup(ModelReader.class).read(in, null); + } catch(IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_pom, e)); + } + } + + public Model readModel(File pomFile) throws CoreException { + try { + BufferedInputStream is = new BufferedInputStream(new FileInputStream(pomFile)); + try { + return readModel(is); + } finally { + IOUtil.close(is); + } + } catch(IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_pom, e)); + } + } + + public void writeModel(Model model, OutputStream out) throws CoreException { + try { + lookup(ModelWriter.class).write(out, null, model); + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_write_pom, ex)); + } + } + + public MavenProject readProject(File pomFile, IProgressMonitor monitor) throws CoreException { + try { + MavenExecutionRequest request = createExecutionRequest(monitor); + lookup(MavenExecutionRequestPopulator.class).populateDefaults(request); + ProjectBuildingRequest configuration = request.getProjectBuildingRequest(); + configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + configuration.setRepositorySession(createRepositorySession(request)); + return lookup(ProjectBuilder.class).build(pomFile, configuration).getProject(); + } catch(ProjectBuildingException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_project, + ex)); + } catch(MavenExecutionRequestPopulationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_project, + ex)); + } + } + + public MavenExecutionResult readProject(MavenExecutionRequest request, IProgressMonitor monitor) throws CoreException { + File pomFile = request.getPom(); + MavenExecutionResult result = new DefaultMavenExecutionResult(); + try { + lookup(MavenExecutionRequestPopulator.class).populateDefaults(request); + ProjectBuildingRequest configuration = request.getProjectBuildingRequest(); + configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + configuration.setRepositorySession(createRepositorySession(request)); + ProjectBuildingResult projectBuildingResult = lookup(ProjectBuilder.class).build(pomFile, configuration); + result.setProject(projectBuildingResult.getProject()); + result.setDependencyResolutionResult(projectBuildingResult.getDependencyResolutionResult()); + } catch(ProjectBuildingException ex) { + //don't add the exception here. this should come out as a build marker, not fill + //the error logs with msgs + return result.addException(ex); + } catch(MavenExecutionRequestPopulationException ex) { + return result.addException(ex); + } + return result; + } + + /** + * Makes MavenProject instances returned by #readProject methods suitable for caching and reuse with other + * MavenSession instances.<br/> + * Do note that MavenProject.getParentProject() cannot be used for detached MavenProject instances. Use + * #resolveParentProject to resolve parent project instance. + */ + public void detachFromSession(MavenProject project) throws CoreException { + try { + // TODO remove reflection when we have embedder 3.0.1 or better + Field f = project.getClass().getDeclaredField("projectBuilderConfiguration"); //$NON-NLS-1$ + f.setAccessible(true); + ProjectBuildingRequest request; + request = (ProjectBuildingRequest) f.get(project); + request.setRepositorySession(lookup(ContextRepositorySystemSession.class)); + } catch(NoSuchFieldException ex) { + MavenLogger.log(ex.getMessage(), ex); + } catch(IllegalAccessException ex) { + MavenLogger.log(ex.getMessage(), ex); + } + } + + public MavenProject resolveParentProject(MavenExecutionRequest request, MavenProject child, IProgressMonitor monitor) + throws CoreException { + ProjectBuildingRequest configuration = request.getProjectBuildingRequest(); + configuration.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + configuration.setRepositorySession(createRepositorySession(request)); + + try { + configuration.setRemoteRepositories(child.getRemoteArtifactRepositories()); + + File parentFile = child.getParentFile(); + if(parentFile != null) { + return lookup(ProjectBuilder.class).build(parentFile, configuration).getProject(); + } + + Artifact parentArtifact = child.getParentArtifact(); + if(parentArtifact != null) { + return lookup(ProjectBuilder.class).build(parentArtifact, configuration).getProject(); + } + } catch(ProjectBuildingException ex) { + MavenLogger.log("Could not read parent project", ex); + } + + return null; + } + + public Artifact resolve(String groupId, String artifactId, String version, String type, String classifier, + List<ArtifactRepository> remoteRepositories, IProgressMonitor monitor) throws CoreException { + Artifact artifact = lookup(RepositorySystem.class).createArtifactWithClassifier(groupId, artifactId, version, type, + classifier); + + if(remoteRepositories == null) { + try { + remoteRepositories = getArtifactRepositories(); + } catch(CoreException e) { + // we've tried + remoteRepositories = Collections.emptyList(); + } + } + + ArtifactRepository localRepository = getLocalRepository(); + + org.sonatype.aether.RepositorySystem repoSystem = lookup(org.sonatype.aether.RepositorySystem.class); + + MavenRepositorySystemSession session = new MavenRepositorySystemSession(); + session.setLocalRepositoryManager(repoSystem.newLocalRepositoryManager(new LocalRepository(localRepository + .getBasedir()))); + session.setTransferListener(createArtifactTransferListener(monitor)); + + ArtifactRequest request = new ArtifactRequest(); + request.setArtifact(RepositoryUtils.toArtifact(artifact)); + request.setRepositories(RepositoryUtils.toRepos(remoteRepositories)); + + ArtifactResult result; + try { + result = repoSystem.resolveArtifact(session, request); + } catch(ArtifactResolutionException ex) { + result = ex.getResults().get(0); + } + + setLastUpdated(localRepository, remoteRepositories, artifact); + + if(result.isResolved()) { + artifact.selectVersion(result.getArtifact().getVersion()); + artifact.setFile(result.getArtifact().getFile()); + artifact.setResolved(true); + } else { + ArrayList<IStatus> members = new ArrayList<IStatus>(); + for(Exception e : result.getExceptions()) { + if(!(e instanceof ArtifactNotFoundException)) { + members.add(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, e.getMessage(), e)); + } + } + if(members.isEmpty()) { + members.add(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, NLS.bind(Messages.MavenImpl_error_missing, artifact), null)); + } + IStatus[] newMembers = members.toArray(new IStatus[members.size()]); + throw new CoreException(new MultiStatus(IMavenConstants.PLUGIN_ID, -1, newMembers, Messages.MavenImpl_error_resolve, + null)); + } + + return artifact; + } + + public String getArtifactPath(ArtifactRepository repository, String groupId, String artifactId, String version, + String type, String classifier) throws CoreException { + Artifact artifact = lookup(RepositorySystem.class).createArtifactWithClassifier(groupId, artifactId, version, type, + classifier); + return repository.pathOf(artifact); + } + + private void setLastUpdated(ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories, + Artifact artifact) throws CoreException { + + Properties lastUpdated = loadLastUpdated(localRepository, artifact); + + String timestamp = Long.toString(System.currentTimeMillis()); + + for(ArtifactRepository repository : remoteRepositories) { + lastUpdated.setProperty(getLastUpdatedKey(repository, artifact), timestamp); + } + + File lastUpdatedFile = getLastUpdatedFile(localRepository, artifact); + try { + lastUpdatedFile.getParentFile().mkdirs(); + BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(lastUpdatedFile)); + try { + lastUpdated.store(os, null); + } finally { + IOUtil.close(os); + } + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_write_lastUpdated, ex)); + } + } + + /** + * This is a temporary implementation that only works for artifacts resolved using #resolve. + */ + public boolean isUnavailable(String groupId, String artifactId, String version, String type, String classifier, + List<ArtifactRepository> remoteRepositories) throws CoreException { + Artifact artifact = lookup(RepositorySystem.class).createArtifactWithClassifier(groupId, artifactId, version, type, + classifier); + + ArtifactRepository localRepository = getLocalRepository(); + + File artifactFile = new File(localRepository.getBasedir(), localRepository.pathOf(artifact)); + + if(artifactFile.canRead()) { + // artifact is available locally + return false; + } + + if(remoteRepositories == null || remoteRepositories.isEmpty()) { + // no remote repositories + return true; + } + + // now is the hard part + Properties lastUpdated = loadLastUpdated(localRepository, artifact); + + for(ArtifactRepository repository : remoteRepositories) { + String timestamp = lastUpdated.getProperty(getLastUpdatedKey(repository, artifact)); + if(timestamp == null) { + // availability of the artifact from this repository has not been checked yet + return false; + } + } + + // artifact is not available locally and all remote repositories have been checked in the past + return true; + } + + private String getLastUpdatedKey(ArtifactRepository repository, Artifact artifact) { + StringBuilder key = new StringBuilder(); + + // repository part + key.append(repository.getId()); + if(repository.getAuthentication() != null) { + key.append('|').append(repository.getAuthentication().getUsername()); + } + key.append('|').append(repository.getUrl()); + + // artifact part + key.append('|').append(artifact.getClassifier()); + + return key.toString(); + } + + private Properties loadLastUpdated(ArtifactRepository localRepository, Artifact artifact) throws CoreException { + Properties lastUpdated = new Properties(); + File lastUpdatedFile = getLastUpdatedFile(localRepository, artifact); + try { + BufferedInputStream is = new BufferedInputStream(new FileInputStream(lastUpdatedFile)); + try { + lastUpdated.load(is); + } finally { + IOUtil.close(is); + } + } catch(FileNotFoundException ex) { + // that's okay + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_read_lastUpdated, ex)); + } + return lastUpdated; + } + + private File getLastUpdatedFile(ArtifactRepository localRepository, Artifact artifact) { + return new File(localRepository.getBasedir(), basePathOf(localRepository, artifact) + "/" //$NON-NLS-1$ + + "m2e-lastUpdated.properties"); //$NON-NLS-1$ + } + + private static final char PATH_SEPARATOR = '/'; + + private static final char GROUP_SEPARATOR = '.'; + + private String basePathOf(ArtifactRepository repository, Artifact artifact) { + StringBuilder path = new StringBuilder(128); + + path.append(formatAsDirectory(artifact.getGroupId())).append(PATH_SEPARATOR); + path.append(artifact.getArtifactId()).append(PATH_SEPARATOR); + path.append(artifact.getBaseVersion()).append(PATH_SEPARATOR); + + return path.toString(); + } + + private String formatAsDirectory(String directory) { + return directory.replace(GROUP_SEPARATOR, PATH_SEPARATOR); + } + + public <T> T getMojoParameterValue(MavenSession session, MojoExecution mojoExecution, String parameter, + Class<T> asType) throws CoreException { + + try { + MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); + + ClassRealm pluginRealm = lookup(BuildPluginManager.class).getPluginRealm(session, + mojoDescriptor.getPluginDescriptor()); + + ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); + + ConfigurationConverter typeConverter = converterLookup.lookupConverterForType(asType); + + Xpp3Dom dom = mojoExecution.getConfiguration(); + + if(dom == null) { + return null; + } + + PlexusConfiguration pomConfiguration = new XmlPlexusConfiguration(dom); + + PlexusConfiguration configuration = pomConfiguration.getChild(parameter); + + if(configuration == null) { + return null; + } + + Object value = typeConverter.fromConfiguration(converterLookup, configuration, asType, + mojoDescriptor.getImplementationClass(), pluginRealm, expressionEvaluator, null); + return asType.cast(value); + } catch(ComponentConfigurationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(PluginManagerException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(PluginResolutionException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } + } + + public <T> T getMojoParameterValue(String parameter, Class<T> type, MavenSession session, Plugin plugin, + ConfigurationContainer configuration, String goal) throws CoreException { + Xpp3Dom config = (Xpp3Dom) configuration.getConfiguration(); + config = (config != null) ? config.getChild(parameter) : null; + + PlexusConfiguration paramConfig = null; + + if(config == null) { + MojoDescriptor mojoDescriptor; + + try { + mojoDescriptor = lookup(BuildPluginManager.class).getMojoDescriptor(plugin, goal, + session.getCurrentProject().getRemotePluginRepositories(), session.getRepositorySession()); + } catch(PluginNotFoundException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(PluginResolutionException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(PluginDescriptorParsingException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(MojoNotFoundException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(InvalidPluginDescriptorException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } + + PlexusConfiguration defaultConfig = mojoDescriptor.getMojoConfiguration(); + if(defaultConfig != null) { + paramConfig = defaultConfig.getChild(parameter, false); + } + } else { + paramConfig = new XmlPlexusConfiguration(config); + } + + if(paramConfig == null) { + return null; + } + + try { + MojoExecution mojoExecution = new MojoExecution(plugin, goal, "default"); //$NON-NLS-1$ + + ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); + + ConfigurationConverter typeConverter = converterLookup.lookupConverterForType(type); + + Object value = typeConverter.fromConfiguration(converterLookup, paramConfig, type, Object.class, + plexus.getContainerRealm(), expressionEvaluator, null); + return type.cast(value); + } catch(ComponentConfigurationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } catch(ClassCastException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_param, ex)); + } + } + + @SuppressWarnings("deprecation") + public void xxxRemoveExtensionsRealm(MavenProject project) { + ClassRealm realm = project.getClassRealm(); + if(realm != null && realm != plexus.getContainerRealm()) { + ClassWorld world = ((MutablePlexusContainer) plexus).getClassWorld(); + try { + world.disposeRealm(realm.getId()); + } catch(NoSuchRealmException ex) { + MavenLogger.log("Could not dispose of project extensions class realm", ex); + } + } + } + + public ArtifactRepository createArtifactRepository(String id, String url) throws CoreException { + Repository repository = new Repository(); + repository.setId(id); + repository.setUrl(url); + repository.setLayout("default"); //$NON-NLS-1$ + + ArtifactRepository repo; + try { + repo = lookup(RepositorySystem.class).buildArtifactRepository(repository); + ArrayList<ArtifactRepository> repos = new ArrayList<ArtifactRepository>(Arrays.asList(repo)); + injectSettings(repos); + } catch(InvalidRepositoryException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_create_repo, ex)); + } + return repo; + } + + public List<ArtifactRepository> getArtifactRepositories() throws CoreException { + return getArtifactRepositories(true); + } + + public List<ArtifactRepository> getArtifactRepositories(boolean injectSettings) throws CoreException { + ArrayList<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>(); + for(Profile profile : getActiveProfiles()) { + addArtifactRepositories(repositories, profile.getRepositories()); + } + + addDefaultRepository(repositories); + + if(injectSettings) { + injectSettings(repositories); + } + + return removeDuplicateRepositories(repositories); + } + + private List<ArtifactRepository> removeDuplicateRepositories(ArrayList<ArtifactRepository> repositories) { + ArrayList<ArtifactRepository> result = new ArrayList<ArtifactRepository>(); + + HashSet<String> keys = new HashSet<String>(); + for(ArtifactRepository repository : repositories) { + StringBuilder key = new StringBuilder(); + if(repository.getId() != null) { + key.append(repository.getId()); + } + key.append(':').append(repository.getUrl()).append(':'); + if(repository.getAuthentication() != null && repository.getAuthentication().getUsername() != null) { + key.append(repository.getAuthentication().getUsername()); + } + if(keys.add(key.toString())) { + result.add(repository); + } + } + return result; + } + + private void injectSettings(ArrayList<ArtifactRepository> repositories) throws CoreException { + Settings settings = getSettings(); + + lookup(RepositorySystem.class).injectMirror(repositories, getMirrors()); + lookup(RepositorySystem.class).injectProxy(repositories, settings.getProxies()); + lookup(RepositorySystem.class).injectAuthentication(repositories, settings.getServers()); + } + + private void addDefaultRepository(ArrayList<ArtifactRepository> repositories) throws CoreException { + for(ArtifactRepository repository : repositories) { + if(RepositorySystem.DEFAULT_REMOTE_REPO_ID.equals(repository.getId())) { + return; + } + } + try { + repositories.add(0, lookup(RepositorySystem.class).createDefaultRemoteRepository()); + } catch(InvalidRepositoryException ex) { + MavenLogger.log("Unexpected exception", ex); + } + } + + private void addArtifactRepositories(ArrayList<ArtifactRepository> artifactRepositories, List<Repository> repositories) + throws CoreException { + for(Repository repository : repositories) { + try { + ArtifactRepository artifactRepository = lookup(RepositorySystem.class).buildArtifactRepository(repository); + artifactRepositories.add(artifactRepository); + } catch(InvalidRepositoryException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_read_settings, + ex)); + } + } + } + + private List<Profile> getActiveProfiles() throws CoreException { + Settings settings = getSettings(); + List<String> activeProfilesIds = settings.getActiveProfiles(); + ArrayList<Profile> activeProfiles = new ArrayList<Profile>(); + for(org.apache.maven.settings.Profile settingsProfile : settings.getProfiles()) { + if((settingsProfile.getActivation() != null && settingsProfile.getActivation().isActiveByDefault()) + || activeProfilesIds.contains(settingsProfile.getId())) { + Profile profile = SettingsUtils.convertFromSettingsProfile(settingsProfile); + activeProfiles.add(profile); + } + } + return activeProfiles; + } + + public List<ArtifactRepository> getPluginArtifactRepositories() throws CoreException { + return getPluginArtifactRepositories(true); + } + + public List<ArtifactRepository> getPluginArtifactRepositories(boolean injectSettings) throws CoreException { + ArrayList<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>(); + for(Profile profile : getActiveProfiles()) { + addArtifactRepositories(repositories, profile.getPluginRepositories()); + } + addDefaultRepository(repositories); + + if(injectSettings) { + injectSettings(repositories); + } + + return removeDuplicateRepositories(repositories); + } + + public Mirror getMirror(ArtifactRepository repo) throws CoreException { + MavenExecutionRequest request = createExecutionRequest(new NullProgressMonitor()); + populateDefaults(request); + return lookup(RepositorySystem.class).getMirror(repo, request.getMirrors()); + }; + + public void populateDefaults(MavenExecutionRequest request) throws CoreException { + try { + lookup(MavenExecutionRequestPopulator.class).populateDefaults(request); + } catch(MavenExecutionRequestPopulationException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_read_config, ex)); + } + } + + public List<Mirror> getMirrors() throws CoreException { + MavenExecutionRequest request = createExecutionRequest(null); + populateDefaults(request); + return request.getMirrors(); + } + + public void addSettingsChangeListener(ISettingsChangeListener listener) { + settingsListeners.add(listener); + } + + public void removeSettingsChangeListener(ISettingsChangeListener listener) { + settingsListeners.remove(listener); + } + + public void addLocalRepositoryListener(ILocalRepositoryListener listener) { + localRepositoryListeners.add(listener); + } + + public void removeLocalRepositoryListener(ILocalRepositoryListener listener) { + localRepositoryListeners.remove(listener); + } + + public List<ILocalRepositoryListener> getLocalRepositoryListeners() { + return localRepositoryListeners; + } + + @SuppressWarnings("deprecation") + public WagonTransferListenerAdapter createTransferListener(IProgressMonitor monitor) { + return new WagonTransferListenerAdapter(this, monitor, console); + } + + public TransferListener createArtifactTransferListener(IProgressMonitor monitor) { + return new ArtifactTransferListenerAdapter(this, monitor, console); + } + + public synchronized PlexusContainer getPlexusContainer() throws CoreException { + if(plexus == null) { + try { + plexus = newPlexusContainer(); + plexus.setLoggerManager(new EclipseLoggerManager(console, mavenConfiguration)); + } catch(PlexusContainerException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_init_maven, ex)); + } + } + return plexus; + } + + public ProxyInfo getProxyInfo(String protocol) throws CoreException { + Settings settings = getSettings(); + + for(Proxy proxy : settings.getProxies()) { + if(proxy.isActive() && protocol.equalsIgnoreCase(proxy.getProtocol())) { + ProxyInfo proxyInfo = new ProxyInfo(); + proxyInfo.setType(proxy.getProtocol()); + proxyInfo.setHost(proxy.getHost()); + proxyInfo.setPort(proxy.getPort()); + proxyInfo.setNonProxyHosts(proxy.getNonProxyHosts()); + proxyInfo.setUserName(proxy.getUsername()); + proxyInfo.setPassword(proxy.getPassword()); + return proxyInfo; + } + } + + return null; + } + + public List<MavenProject> getSortedProjects(List<MavenProject> projects) throws CoreException { + try { + ProjectSorter rm = new ProjectSorter(projects); + return rm.getSortedProjects(); + } catch(CycleDetectedException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, Messages.MavenImpl_error_sort, ex)); + } catch(DuplicateProjectException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, Messages.MavenImpl_error_sort, ex)); + } + } + + public String resolvePluginVersion(String groupId, String artifactId, MavenSession session) throws CoreException { + Plugin plugin = new Plugin(); + plugin.setGroupId(groupId); + plugin.setArtifactId(artifactId); + PluginVersionRequest request = new DefaultPluginVersionRequest(plugin, session); + try { + return lookup(PluginVersionResolver.class).resolve(request).getVersion(); + } catch(PluginVersionResolutionException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, ex.getMessage(), ex)); + } + } + + private <T> T lookup(Class<T> clazz) throws CoreException { + try { + return getPlexusContainer().lookup(clazz); + } catch(ComponentLookupException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.MavenImpl_error_lookup, ex)); + } + } + + private static DefaultPlexusContainer newPlexusContainer() throws PlexusContainerException { + ContainerConfiguration mavenCoreCC = new DefaultContainerConfiguration().setClassWorld( + new ClassWorld(MAVEN_CORE_REALM_ID, ClassWorld.class.getClassLoader())).setName( + "mavenCore"); //$NON-NLS-1$ + + mavenCoreCC.setAutoWiring(true); + + return new DefaultPlexusContainer(mavenCoreCC, new ExtensionModule()); + } + + public synchronized void disposeContainer() { + if(plexus != null) { + plexus.dispose(); + } + } + + public ClassLoader getProjectRealm(MavenProject project) { + ClassLoader classLoader = project.getClassRealm(); + if(classLoader == null) { + classLoader = plexus.getContainerRealm(); + } + return classLoader; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenWorkspaceRuntime.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenWorkspaceRuntime.java new file mode 100644 index 00000000..f7db67ec --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/MavenWorkspaceRuntime.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.File; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMavenLauncherConfiguration; +import org.eclipse.m2e.core.embedder.MavenRuntime; +import org.eclipse.m2e.core.embedder.MavenRuntimeManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; + +/** + * Maven 3.0-SNAPSHOT runtime loaded from the Eclipse Workspace + * + * @author Eugene Kuleshov + * @author Igor Fedorenko + */ +public class MavenWorkspaceRuntime implements MavenRuntime { + + private static final ArtifactKey MAVEN_DISTRIBUTION = new ArtifactKey("org.apache.maven", "apache-maven", "3.0", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + private static final ArtifactKey PLEXUS_CLASSWORLDS = new ArtifactKey("org.codehaus.plexus", "plexus-classworlds", null, null); //$NON-NLS-1$ //$NON-NLS-2$ + + private static final String MAVEN_EXECUTOR_CLASS = "org.apache.maven.cli.MavenCli"; //$NON-NLS-1$ + + private static final String PLEXUS_CLASSWORLD_NAME = "plexus.core"; //$NON-NLS-1$ + + private MavenProjectManager projectManager; + + public MavenWorkspaceRuntime(MavenProjectManager projectManager) { + this.projectManager = projectManager; + } + + public String getLocation() { + return MavenRuntimeManager.WORKSPACE; + } + + public String getSettings() { + return null; + } + + public boolean isEditable() { + return false; + } + + public boolean isAvailable() { + return getMavenDistribution() != null; + } + + private IMavenProjectFacade getMavenDistribution() { + for (IMavenProjectFacade facade : projectManager.getProjects()) { + ArtifactKey artifactKey = facade.getArtifactKey(); + if (MAVEN_DISTRIBUTION.getGroupId().equals(artifactKey.getGroupId()) // + && MAVEN_DISTRIBUTION.getArtifactId().equals(artifactKey.getArtifactId())// + && artifactKey.getVersion().startsWith(MAVEN_DISTRIBUTION.getVersion())) { + return facade; + } + } + return null; + } + + public void createLauncherConfiguration(IMavenLauncherConfiguration collector, IProgressMonitor monitor) throws CoreException { + IMavenProjectFacade maven = getMavenDistribution(); + if (maven != null) { + MavenProject mavenProject = maven.getMavenProject(monitor); + + collector.setMainType(MAVEN_EXECUTOR_CLASS, PLEXUS_CLASSWORLD_NAME); + + collector.addRealm(PLEXUS_CLASSWORLD_NAME); + + Set<Artifact> artifacts = mavenProject.getArtifacts(); + + Artifact launcherArtifact = null; + + for (Artifact artifact : artifacts) { + if (Artifact.SCOPE_TEST.equals(artifact.getScope())) { + continue; + } + + if (PLEXUS_CLASSWORLDS.getGroupId().equals(artifact.getGroupId()) && PLEXUS_CLASSWORLDS.getArtifactId().equals(artifact.getArtifactId())) { + launcherArtifact = artifact; + continue; + } + + addArtifact(collector, artifact); + } + + if (launcherArtifact != null) { + collector.addRealm(IMavenLauncherConfiguration.LAUNCHER_REALM); + addArtifact(collector, launcherArtifact); + } + } + + // XXX throw something at the caller! + } + + private void addArtifact(IMavenLauncherConfiguration collector, Artifact artifact) throws CoreException { + IMavenProjectFacade facade = projectManager.getMavenProject(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); + + if (facade != null) { + collector.addProjectEntry(facade); + } else { + File file = artifact.getFile(); + if (file != null) { + collector.addArchiveEntry(file.getAbsolutePath()); + } + } + } + + public String toString() { + return "Workspace (" + getVersion() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public String getVersion() { + IMavenProjectFacade maven = getMavenDistribution(); + if (maven != null) { + return maven.getArtifactKey().getVersion(); + } + return MAVEN_DISTRIBUTION.getVersion(); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/WagonTransferListenerAdapter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/WagonTransferListenerAdapter.java new file mode 100644 index 00000000..7d5b19c5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/embedder/WagonTransferListenerAdapter.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.embedder; + +import java.io.File; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.index.artifact.Gav; +import org.apache.maven.index.artifact.GavCalculator; +import org.apache.maven.index.artifact.M2GavCalculator; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.events.TransferEvent; +import org.apache.maven.wagon.events.TransferListener; +import org.apache.maven.wagon.repository.Repository; + +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ILocalRepositoryListener; + +/** + * @author Eugene Kuleshov + */ +final class WagonTransferListenerAdapter extends AbstractTransferListenerAdapter implements TransferListener { + // TODO this is just wrong! + private final GavCalculator gavCalculator = new M2GavCalculator(); + + WagonTransferListenerAdapter(MavenImpl maven, IProgressMonitor monitor, MavenConsole console) { + super(maven, monitor, console); + } + + public void transferInitiated(TransferEvent e) { + // System.err.println( "init "+e.getWagon().getRepository()+"/"+e.getResource().getName()); + transferInitiated((String) null); + } + + public void transferStarted(TransferEvent e) { + StringBuilder sb = new StringBuilder(); + if(e.getWagon() != null && e.getWagon().getRepository() != null) { + Wagon wagon = e.getWagon(); + Repository repository = wagon.getRepository(); + String repositoryId = repository.getId(); + sb.append(repositoryId).append(" : "); //$NON-NLS-1$ + } + sb.append(e.getResource().getName()); + transferStarted(sb.toString()); + } + + public void transferProgress(TransferEvent e, byte[] buffer, int length) { + long total = e.getResource().getContentLength(); + String artifactUrl = e.getWagon().getRepository() + "/" + e.getResource().getName(); //$NON-NLS-1$ + + transferProgress(artifactUrl, total, length); + } + + public void transferCompleted(TransferEvent e) { + String artifactUrl = e.getWagon().getRepository() + "/" + e.getResource().getName(); //$NON-NLS-1$ + transferCompleted(artifactUrl); + + notifyLocalRepositoryListeners(e); + } + + public void transferError(TransferEvent e) { + transferError(e.getWagon().getRepository() + "/" + e.getResource().getName(), e.getException()); //$NON-NLS-1$ + } + + public void debug(String message) { + // System.err.println( "debug "+message); + } + + private void notifyLocalRepositoryListeners(TransferEvent e) { + try { + ArtifactRepository localRepository = maven.getLocalRepository(); + + if (!(localRepository.getLayout() instanceof DefaultRepositoryLayout)) { + return; + } + + String repoBasepath = new File(localRepository.getBasedir()).getCanonicalPath(); + + File artifactFile = e.getLocalFile(); + + if (artifactFile == null) { + return; + } + + String artifactPath = artifactFile.getCanonicalPath(); + if (!artifactPath.startsWith(repoBasepath)) { + return; + } + + artifactPath = artifactPath.substring(repoBasepath.length()); + Gav gav = gavCalculator.pathToGav(artifactPath); + ArtifactKey artifactKey = new ArtifactKey(gav.getGroupId(), gav.getArtifactId(), gav.getVersion(), gav.getClassifier()); + + File repoBasedir = new File(localRepository.getBasedir()).getCanonicalFile(); + + for (ILocalRepositoryListener listener : maven.getLocalRepositoryListeners()) { + listener.artifactInstalled(repoBasedir, artifactKey, artifactFile); + } + } catch (Exception ex) { + MavenLogger.log("Could not notify local repository listeners", ex); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/ArtifactScanningMonitor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/ArtifactScanningMonitor.java new file mode 100644 index 00000000..adf3f0de --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/ArtifactScanningMonitor.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.index.ArtifactContext; +import org.apache.maven.index.ArtifactScanningListener; +import org.apache.maven.index.ScanningResult; +import org.apache.maven.index.context.IndexingContext; + +import org.eclipse.m2e.core.core.MavenConsole; + +class ArtifactScanningMonitor implements ArtifactScanningListener { + + private static final long THRESHOLD = 1 * 1000L; + + //private final IndexInfo indexInfo; + + private final IProgressMonitor monitor; + + private final MavenConsole console; + + private long timestamp = System.currentTimeMillis(); + + private File repositoryDir; + + ArtifactScanningMonitor(File repositoryDir, IProgressMonitor monitor, MavenConsole console) { + //this.indexInfo = indexInfo; + this.repositoryDir = repositoryDir; + this.monitor = monitor; + this.console = console; + } + + public void scanningStarted(IndexingContext ctx) { + } + + public void scanningFinished(IndexingContext ctx, ScanningResult result) { + } + + public void artifactDiscovered(ArtifactContext ac) { + long current = System.currentTimeMillis(); + if((current - timestamp) > THRESHOLD) { + // String id = info.groupId + ":" + info.artifactId + ":" + info.version; + String id = ac.getPom().getAbsolutePath().substring( + this.repositoryDir.getAbsolutePath().length()); + this.monitor.setTaskName(id); + this.timestamp = current; + } + } + + public void artifactError(ArtifactContext ac, Exception e) { + String id = ac.getPom().getAbsolutePath().substring(repositoryDir.getAbsolutePath().length()); + console.logError(id + " " + e.getMessage()); //$NON-NLS-1$ + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/AsyncFetcher.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/AsyncFetcher.java new file mode 100644 index 00000000..458df72d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/AsyncFetcher.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.HttpURLConnection; + +import com.ning.http.client.AsyncHandler; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.AsyncHttpClientConfig; +import com.ning.http.client.HttpResponseBodyPart; +import com.ning.http.client.HttpResponseHeaders; +import com.ning.http.client.HttpResponseStatus; +import com.ning.http.client.ProxyServer; +import com.ning.http.client.Realm; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.io.RawInputStreamFacade; + +import org.apache.maven.index.updater.AbstractResourceFetcher; +import org.apache.maven.wagon.authentication.AuthenticationInfo; +import org.apache.maven.wagon.proxy.ProxyInfo; +import org.apache.maven.wagon.proxy.ProxyUtils; +import org.apache.maven.wagon.repository.Repository; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * A resource fetcher using Async HTTP Client. + * + * @author Benjamin Bentmann + */ +class AsyncFetcher extends AbstractResourceFetcher { + + private final AuthenticationInfo authInfo; + + private final ProxyInfo proxyInfo; + + final IProgressMonitor monitor; + + private AsyncHttpClient httpClient; + + private Realm authRealm; + + private ProxyServer proxyServer; + + private String baseUrl; + + public AsyncFetcher(final AuthenticationInfo authInfo, final ProxyInfo proxyInfo, final IProgressMonitor monitor) { + this.authInfo = authInfo; + this.proxyInfo = proxyInfo; + this.monitor = (monitor != null) ? monitor : new NullProgressMonitor(); + } + + private static Realm toRealm(AuthenticationInfo authInfo) { + Realm realm = null; + + if(authInfo != null && authInfo.getUserName() != null && authInfo.getUserName().length() > 0) { + realm = new Realm.RealmBuilder().setPrincipal(authInfo.getUserName()).setPassword(authInfo.getPassword()) + .setUsePreemptiveAuth(false).build(); + } + + return realm; + } + + private static ProxyServer toProxyServer(ProxyInfo proxyInfo) { + ProxyServer proxyServer = null; + + if(proxyInfo != null) { + ProxyServer.Protocol protocol = "https".equalsIgnoreCase(proxyInfo.getType()) ? ProxyServer.Protocol.HTTPS //$NON-NLS-1$ + : ProxyServer.Protocol.HTTP; + proxyServer = new ProxyServer(protocol, proxyInfo.getHost(), proxyInfo.getPort(), proxyInfo.getUserName(), + proxyInfo.getPassword()); + } + + return proxyServer; + } + + private ProxyServer getProxyServer(ProxyInfo proxyInfo, String url) { + if(proxyInfo != null) { + Repository repo = new Repository("id", url); //$NON-NLS-1$ + if(!ProxyUtils.validateNonProxyHosts(proxyInfo, repo.getHost())) { + return toProxyServer(proxyInfo); + } + } + return null; + } + + public void connect(String id, String url) { + AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder(); + configBuilder.setUserAgent("M2Eclipse/" + MavenPlugin.getQualifiedVersion()); //$NON-NLS-1$ + configBuilder.setConnectionTimeoutInMs(15 * 1000); + configBuilder.setRequestTimeoutInMs(60 * 1000); + configBuilder.setCompressionEnabled(true); + configBuilder.setFollowRedirects(true); + + httpClient = new AsyncHttpClient(configBuilder.build()); + + baseUrl = url.endsWith("/") ? url : (url + '/'); //$NON-NLS-1$ + authRealm = toRealm(authInfo); + proxyServer = getProxyServer(proxyInfo, url); + } + + public void disconnect() { + authRealm = null; + proxyServer = null; + baseUrl = null; + if(httpClient != null) { + httpClient.close(); + } + httpClient = null; + } + + @SuppressWarnings("deprecation") + public void retrieve(String name, File targetFile) throws IOException, FileNotFoundException { + InputStream is = retrieve(name); + try { + FileUtils.copyStreamToFile(new RawInputStreamFacade(is), targetFile); + } finally { + IOUtil.close(is); + } + } + + public InputStream retrieve(String name) throws IOException, FileNotFoundException { + String url = buildUrl(baseUrl, name); + + monitor.subTask(NLS.bind(Messages.AsyncFetcher_task_fetching, url)); + + PipedErrorInputStream pis = new PipedErrorInputStream(); + + httpClient.prepareGet(url).setRealm(authRealm).setProxyServer(proxyServer).execute(new RequestHandler(url, pis)); + + return pis; + } + + private static String buildUrl(String baseUrl, String resourceName) { + String url = baseUrl; + + if(resourceName.startsWith("/")) { //$NON-NLS-1$ + url += resourceName.substring(1); + } else { + url += resourceName; + } + + return url; + } + + static final class PipedErrorInputStream extends PipedInputStream { + + private volatile Throwable error; + + public PipedErrorInputStream() { + buffer = new byte[1024 * 128]; + } + + public void setError(Throwable t) { + if(error == null) { + error = t; + } + } + + private void checkError() throws IOException { + if(error != null) { + throw (IOException) new IOException(error.getMessage()).initCause(error); + } + } + + public synchronized int read() throws IOException { + checkError(); + int b = super.read(); + checkError(); + return b; + } + } + + final class RequestHandler implements AsyncHandler<String> { + + private final String url; + + private final PipedErrorInputStream pis; + + private PipedOutputStream pos; + + private long total = -1; + + private long transferred; + + public RequestHandler(String url, PipedErrorInputStream pis) throws IOException { + this.url = url; + this.pis = pis; + pos = new PipedOutputStream(pis); + } + + private void finish(Throwable exception) { + pis.setError(exception); + if(pos != null) { + try { + pos.close(); + } catch(IOException ex) { + // tried it + } + pos = null; + } + } + + private STATE checkCancel() { + if(monitor.isCanceled()) { + finish(new IOException(Messages.AsyncFetcher_error_cancelled)); + return STATE.ABORT; + } + return STATE.CONTINUE; + } + + public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { + if(checkCancel() == STATE.ABORT || pos == null) { + return STATE.ABORT; + } + int bytes = content.getBodyByteBuffer().remaining(); + content.writeTo(pos); + if(total > 0) { + transferred += bytes; + monitor.subTask(NLS.bind(Messages.AsyncFetcher_task_fetching2,url, transferred * 100 / total)); + } + return STATE.CONTINUE; + } + + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { + if(checkCancel() == STATE.ABORT) { + return STATE.ABORT; + } + try { + total = Long.parseLong(headers.getHeaders().getFirstValue("Content-Length")); //$NON-NLS-1$ + } catch(Exception e) { + total = -1; + } + return STATE.CONTINUE; + } + + public STATE onStatusReceived(HttpResponseStatus status) throws Exception { + if(status.getStatusCode() != HttpURLConnection.HTTP_OK) { + finish(new IOException(NLS.bind(Messages.AsyncFetcher_error_server, status.getStatusCode(), status.getStatusText()))); + return STATE.ABORT; + } + if(checkCancel() == STATE.ABORT) { + return STATE.ABORT; + } + return STATE.CONTINUE; + } + + public String onCompleted() throws Exception { + monitor.subTask(""); //$NON-NLS-1$ + finish(null); + return ""; //$NON-NLS-1$ + } + + public void onThrowable(Throwable t) { + finish(t); + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/CompositeIndex.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/CompositeIndex.java new file mode 100644 index 00000000..1d934310 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/CompositeIndex.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.SearchExpression; + + +/** + * CompositeIndex + * + * @author igor + */ +public class CompositeIndex implements IIndex { + + private List<IIndex> indexes; + + public CompositeIndex(List<IIndex> indexes) { + this.indexes = indexes; + } + + public IndexedArtifactFile getIndexedArtifactFile(ArtifactKey artifact) throws CoreException { + for(IIndex index : indexes) { + IndexedArtifactFile aif = index.getIndexedArtifactFile(artifact); + if(aif != null) { + // first one wins + return aif; + } + } + + // did not find anything + return null; + } + + public IndexedArtifactFile identify(File file) throws CoreException { + for(IIndex index : indexes) { + IndexedArtifactFile aif = index.identify(file); + if(aif != null) { + // first one wins + return aif; + } + } + + // did not find anything + return null; + } + + public Collection<IndexedArtifact> find(SearchExpression groupId, SearchExpression artifactId, + SearchExpression version, SearchExpression packaging) throws CoreException { + Set<IndexedArtifact> result = new LinkedHashSet<IndexedArtifact>(); + for(IIndex index : indexes) { + Collection<IndexedArtifact> findResults = index.find(groupId, artifactId, version, packaging); + if(findResults != null) { + result.addAll(findResults); + } + } + return result; + } + + public Collection<IndexedArtifact> find(Collection<SearchExpression> groupId, + Collection<SearchExpression> artifactId, Collection<SearchExpression> version, + Collection<SearchExpression> packaging) throws CoreException { + Set<IndexedArtifact> result = new LinkedHashSet<IndexedArtifact>(); + for(IIndex index : indexes) { + Collection<IndexedArtifact> findResults = index.find(groupId, artifactId, version, packaging); + if(findResults != null) { + result.addAll(findResults); + } + } + return result; + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String searchType) throws CoreException { + Map<String, IndexedArtifact> result = new HashMap<String, IndexedArtifact>(); + for(IIndex index : indexes) { + Map<String, IndexedArtifact> iresult = index.search(term, searchType); + if(iresult != null) { + result.putAll(iresult); + } + } + return result; + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String searchType, int classifier) + throws CoreException { + Map<String, IndexedArtifact> result = new HashMap<String, IndexedArtifact>(); + for(IIndex index : indexes) { + Map<String, IndexedArtifact> iresult = index.search(term, searchType, classifier); + if(iresult != null) { + result.putAll(iresult); + } + } + return result; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLock.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLock.java new file mode 100644 index 00000000..4aa82f6e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLock.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import org.eclipse.core.runtime.internal.adaptor.Locker; + +import org.apache.maven.index.fs.Lock; + +@SuppressWarnings("restriction") +public class EquinoxLock + implements Lock +{ + + private final Locker lock; + + public EquinoxLock( Locker lock ) + { + this.lock = lock; + } + + public void release() + { + lock.release(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLocker.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLocker.java new file mode 100644 index 00000000..e88c7e0c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/EquinoxLocker.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.core.runtime.internal.adaptor.BasicLocation; +import org.eclipse.osgi.util.NLS; + +import org.apache.maven.index.fs.Lock; +import org.apache.maven.index.fs.Locker; + +@SuppressWarnings("restriction") +public class EquinoxLocker + implements Locker +{ + + public Lock lock( File directory ) + throws IOException + { + org.eclipse.core.runtime.internal.adaptor.Locker lock = BasicLocation.createLocker(new File( directory, LOCK_FILE ), null ); + + if ( lock.lock() ) + { + return new EquinoxLock( lock ); + } + + throw new IOException( NLS.bind("Could not acquire lock on directory {0}", directory )); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexUpdaterJob.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexUpdaterJob.java new file mode 100644 index 00000000..a14760f6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexUpdaterJob.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.util.ArrayList; +import java.util.Stack; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.ui.progress.IProgressConstants; + +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.jobs.IBackgroundProcessingQueue; + +class IndexUpdaterJob extends Job implements IBackgroundProcessingQueue { + + public static class IndexUpdaterRule implements ISchedulingRule { + + public boolean contains(ISchedulingRule rule) { + return rule == this; + } + + public boolean isConflicting(ISchedulingRule rule) { + return rule == this; + } + + } + + public interface IndexCommand { + abstract void run(IProgressMonitor monitor) throws CoreException; + } + + private final Stack<IndexUpdaterJob.IndexCommand> updateQueue = new Stack<IndexUpdaterJob.IndexCommand>(); + + public IndexUpdaterJob(NexusIndexManager indexManager, MavenConsole console) { + super(Messages.IndexUpdaterJob_title); + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + setRule(new IndexUpdaterRule()); + } + + public void addCommand(IndexUpdaterJob.IndexCommand indexCommand) { + updateQueue.add(indexCommand); + } + + public IStatus run(IProgressMonitor monitor) { + monitor.beginTask(getName(), IProgressMonitor.UNKNOWN); + + ArrayList<IStatus> problems = new ArrayList<IStatus>(); + + while(!updateQueue.isEmpty()) { + if (monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + IndexUpdaterJob.IndexCommand command = updateQueue.pop(); + try { + command.run(monitor); + } catch(CoreException ex) { + problems.add(ex.getStatus()); + } + } + + monitor.done(); + + return problems.isEmpty()? Status.OK_STATUS: new MultiStatus(IMavenConstants.PLUGIN_ID, -1, problems.toArray(new IStatus[problems.size()]), null, null); + } + + public boolean isEmpty() { + return updateQueue.isEmpty(); + } + +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexedArtifactGroup.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexedArtifactGroup.java new file mode 100644 index 00000000..96dc8e65 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexedArtifactGroup.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.util.LinkedHashMap; + +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.repository.IRepository; + + +public class IndexedArtifactGroup implements Comparable<IndexedArtifactGroup>{ + private final IRepository repository; + private final String prefix; + private final LinkedHashMap<String, IndexedArtifactGroup> nodes = new LinkedHashMap<String, IndexedArtifactGroup>(); + private final LinkedHashMap<String, IndexedArtifact> files = new LinkedHashMap<String, IndexedArtifact>(); + + public IndexedArtifactGroup(IRepository repository, String prefix) { + this.repository = repository; + this.prefix = prefix; + } + + public LinkedHashMap<String, IndexedArtifactGroup> getNodes() { + return nodes; + } + + public LinkedHashMap<String, IndexedArtifact> getFiles() { + return files; + } + + public String getPrefix() { + return prefix; + } + + public IRepository getRepository() { + return this.repository; + } + + /* + * Compare the groups by prefix + */ + public int compareTo(IndexedArtifactGroup o) { + if(o == null){ + return -1; + } + return getPrefix().compareToIgnoreCase(o.getPrefix()); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexesExtensionReader.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexesExtensionReader.java new file mode 100644 index 00000000..3e5bd76e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexesExtensionReader.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.internal.repository.IRepositoryDiscoverer; +import org.eclipse.m2e.core.internal.repository.RepositoryInfo; +import org.eclipse.m2e.core.internal.repository.RepositoryRegistry; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + +/** + * IndexesExtensionReader + * + * @author igor + */ +public class IndexesExtensionReader implements IRepositoryDiscoverer { + + private static final String EXTENSION_INDEXES = IMavenConstants.PLUGIN_ID + ".indexes"; //$NON-NLS-1$ + + private static final String ELEMENT_INDEX = "index"; //$NON-NLS-1$ + + private static final String ATTR_INDEX_ID = "indexId"; //$NON-NLS-1$ + +// private static final String ATTR_INDEX_ARCHIVE = "archive"; + + private static final String ATTR_REPOSITORY_URL = "repositoryUrl"; //$NON-NLS-1$ + +// private static final String ATTR_UPDATE_URL = "updateUrl"; + + private static final String ATTR_IS_SHORT = "isShort"; //$NON-NLS-1$ + + private final NexusIndexManager indexManager; + + public IndexesExtensionReader(NexusIndexManager indexManager) { + this.indexManager = indexManager; + } + + public void addRepositories(RepositoryRegistry registry, IProgressMonitor monitor) throws CoreException { + IExtensionPoint indexesExtensionPoint = Platform.getExtensionRegistry().getExtensionPoint(EXTENSION_INDEXES); + if(indexesExtensionPoint != null) { + IExtension[] indexesExtensions = indexesExtensionPoint.getExtensions(); + for(IExtension extension : indexesExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_INDEX)) { + processIndexElement(registry, element, monitor); + } + } + } + } + } + + private void processIndexElement(RepositoryRegistry registry, IConfigurationElement element, IProgressMonitor monitor) throws CoreException { + String indexId = element.getAttribute(ATTR_INDEX_ID); + String repositoryUrl = element.getAttribute(ATTR_REPOSITORY_URL); + boolean isShort = Boolean.valueOf(element.getAttribute(ATTR_IS_SHORT)).booleanValue(); + +// String indexUpdateUrl = element.getAttribute(ATTR_UPDATE_URL); +// String archive = element.getAttribute(ATTR_INDEX_ARCHIVE); + + RepositoryInfo repository = new RepositoryInfo(indexId, repositoryUrl, IRepositoryRegistry.SCOPE_UNKNOWN, null); + registry.addRepository(repository, monitor); + + // for consistency, always process indexes using our background thread + indexManager.setIndexDetails(repository, isShort? NexusIndex.DETAILS_MIN: NexusIndex.DETAILS_FULL, null/*async*/); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexingTransferListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexingTransferListener.java new file mode 100644 index 00000000..538eeeed --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/IndexingTransferListener.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ILocalRepositoryListener; + +public class IndexingTransferListener implements ILocalRepositoryListener { + + private final NexusIndexManager indexManager; + + public IndexingTransferListener(NexusIndexManager indexManager) { + this.indexManager = indexManager; + } + + public void artifactInstalled(File repositoryBasedir, ArtifactKey artifact, File artifactFile) { + NexusIndex localIndex = indexManager.getLocalIndex(); + if(artifactFile.getName().endsWith(".jar")) { //$NON-NLS-1$ + localIndex.addArtifact(artifactFile, artifact); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndex.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndex.java new file mode 100644 index 00000000..35b8a23e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndex.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; + +import org.apache.maven.index.Field; +import org.apache.maven.index.MAVEN; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IMutableIndex; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.SearchExpression; +import org.eclipse.m2e.core.repository.IRepository; + + +/** + * NexusIndex + * + * @author igor + */ +public class NexusIndex implements IIndex, IMutableIndex { + + /** + * Repository index is disabled. + */ + public static final String DETAILS_DISABLED = "off"; //$NON-NLS-1$ + + /** + * Only artifact index information is used. Classname index is disabled. + */ + public static final String DETAILS_MIN = "min"; //$NON-NLS-1$ + + /** + * Both artifact and classname indexes are used. + */ + public static final String DETAILS_FULL = "full"; //$NON-NLS-1$ + + private final NexusIndexManager indexManager; + + private final IRepository repository; + + private final String indexDetails; + + NexusIndex(NexusIndexManager indexManager, IRepository repository, String indexDetails) { + this.indexManager = indexManager; + this.repository = repository; + this.indexDetails = indexDetails; + } + + public String getRepositoryUrl() { + return this.repository.getUrl(); + } + + public String getIndexDetails() { + return this.indexDetails; + } + + public void addArtifact(File pomFile, ArtifactKey artifactKey) { + indexManager.addDocument(repository, pomFile, artifactKey); + } + + public void removeArtifact(File pomFile, ArtifactKey artifactKey) { + indexManager.removeDocument(repository, pomFile, artifactKey, null); + } + + public Collection<IndexedArtifact> find(SearchExpression groupId, SearchExpression artifactId, + SearchExpression version, SearchExpression packaging) throws CoreException { + return find(wrapIfNotNull(groupId), wrapIfNotNull(artifactId), wrapIfNotNull(version), wrapIfNotNull(packaging)); + } + + /** + * Method wrapping one SearchExpression into a collection, if it is not null. + * + * @param sex + * @return + */ + private Collection<SearchExpression> wrapIfNotNull(SearchExpression se) { + if(se == null) { + return null; + } + return Collections.singleton(se); + } + + public Collection<IndexedArtifact> find(Collection<SearchExpression> groupId, + Collection<SearchExpression> artifactId, Collection<SearchExpression> version, + Collection<SearchExpression> packaging) throws CoreException { + BooleanQuery query = new BooleanQuery(); + + addQueryFromSearchExpressionCollection(query, MAVEN.PACKAGING, packaging); + + addQueryFromSearchExpressionCollection(query, MAVEN.GROUP_ID, groupId); + + addQueryFromSearchExpressionCollection(query, MAVEN.ARTIFACT_ID, artifactId); + + addQueryFromSearchExpressionCollection(query, MAVEN.VERSION, version); + + return indexManager.search(repository, query).values(); + } + + private void addQueryFromSearchExpressionCollection(final BooleanQuery query, final Field field, + final Collection<SearchExpression> sec) { + if(sec != null && !sec.isEmpty()) { + if(sec.size() > 1) { + BooleanQuery q = new BooleanQuery(); + for(SearchExpression se : sec) { + q.add(indexManager.constructQuery(field, se), Occur.SHOULD); + } + query.add(q, Occur.MUST); + } else { + query.add(indexManager.constructQuery(field, sec.iterator().next()), Occur.MUST); + } + } + } + + public IndexedArtifactFile getIndexedArtifactFile(ArtifactKey artifact) throws CoreException { + return indexManager.getIndexedArtifactFile(repository, artifact); + } + + public IndexedArtifactFile identify(File file) throws CoreException { + return indexManager.identify(repository, file); + } + + public void updateIndex(boolean force, IProgressMonitor monitor) throws CoreException { + indexManager.updateIndex(repository, force, monitor); + } + + public void scheduleIndexUpdate(boolean force) { + indexManager.scheduleIndexUpdate(repository, force); + } + + public IndexedArtifactGroup[] getRootIndexedArtifactGroups() throws CoreException { + return indexManager.getRootIndexedArtifactGroups(repository); + } + + public boolean isUpdating() { + return indexManager.isUpdatingIndex(repository); + } + + public IRepository getRepository() { + return repository; + } + + public boolean isEnabled() { + return DETAILS_MIN.equals(indexDetails) || DETAILS_FULL.equals(indexDetails); + } + + public void setIndexDetails(String details) throws CoreException { + indexManager.setIndexDetails(repository, details, null/*async*/); + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String searchType) throws CoreException { + return indexManager.search(getRepository(), term, searchType); + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String searchType, int classifier) + throws CoreException { + return indexManager.search(getRepository(), term, searchType, classifier); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndexManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndexManager.java new file mode 100644 index 00000000..d41af33b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/index/NexusIndexManager.java @@ -0,0 +1,1312 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.index; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; +import java.util.WeakHashMap; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; + +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.FilteredQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryWrapperFilter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; + +import org.apache.maven.index.ArtifactContext; +import org.apache.maven.index.ArtifactContextProducer; +import org.apache.maven.index.ArtifactInfo; +import org.apache.maven.index.Field; +import org.apache.maven.index.IteratorSearchRequest; +import org.apache.maven.index.IteratorSearchResponse; +import org.apache.maven.index.MAVEN; +import org.apache.maven.index.NexusIndexer; +import org.apache.maven.index.SearchType; +import org.apache.maven.index.artifact.Gav; +import org.apache.maven.index.artifact.IllegalArtifactCoordinateException; +import org.apache.maven.index.context.IndexCreator; +import org.apache.maven.index.context.IndexingContext; +import org.apache.maven.index.creator.JarFileContentsIndexCreator; +import org.apache.maven.index.creator.MavenArchetypeArtifactInfoIndexCreator; +import org.apache.maven.index.creator.MavenPluginArtifactInfoIndexCreator; +import org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator; +import org.apache.maven.index.fs.Lock; +import org.apache.maven.index.updater.IndexUpdateRequest; +import org.apache.maven.index.updater.IndexUpdateResult; +import org.apache.maven.index.updater.IndexUpdater; +import org.apache.maven.wagon.authentication.AuthenticationInfo; +import org.apache.maven.wagon.proxy.ProxyInfo; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ArtifactRepositoryRef; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexListener; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.MatchTyped; +import org.eclipse.m2e.core.index.MatchTyped.MatchType; +import org.eclipse.m2e.core.index.SearchExpression; +import org.eclipse.m2e.core.index.SourcedSearchExpression; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.IndexUpdaterJob.IndexCommand; +import org.eclipse.m2e.core.internal.repository.IRepositoryIndexer; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + + +/** + * @author Eugene Kuleshov + */ +public class NexusIndexManager implements IndexManager, IMavenProjectChangedListener, IRepositoryIndexer { + + public static final int MIN_CLASS_QUERY_LENGTH = 6; + + /** + * Lazy instantiated nexus indexer instance. + */ + private NexusIndexer indexer; + + /** + * Lazy instantiated nexus indexer's contextProducer. + */ + private ArtifactContextProducer artifactContextProducer; + + /** + * Lock guarding lazy instantiation of indexerLock instance + */ + private final Object indexerLock = new Object(); + + /** + * Lock guarding lazy instantiation of contextProducer instance + */ + private final Object contextProducerLock = new Object(); + + private IMaven maven; + + private MavenProjectManager projectManager; + + private IRepositoryRegistry repositoryRegistry; + + private ArrayList<IndexCreator> fullCreators = null; + + private ArrayList<IndexCreator> minCreators = null; + + private final MavenConsole console; + + private final File baseIndexDir; + + private final List<IndexListener> indexListeners = new ArrayList<IndexListener>(); + + private NexusIndex localIndex; + + private final NexusIndex workspaceIndex; + + private final IndexUpdaterJob updaterJob; + + private Properties indexDetails = new Properties(); + + private Set<String> updatingIndexes = new HashSet<String>(); + + private IndexUpdater indexUpdater; + + private static final EquinoxLocker locker = new EquinoxLocker(); + + /** + * Maps repository UID to the lock object associated with the repository. Entries are only added but never directly + * removed from the map, although jvm garbage collector may remove otherwise unused entries to reclaim the little + * memory they use. Never access this map directly. #getIndexLock must be used to get repository lock object. + */ + private final Map<String, Object> indexLocks = new WeakHashMap<String, Object>(); + + public NexusIndexManager(MavenConsole console, MavenProjectManager projectManager, + IRepositoryRegistry repositoryRegistry, File stateDir) { + this.console = console; + this.projectManager = projectManager; + this.repositoryRegistry = repositoryRegistry; + this.baseIndexDir = new File(stateDir, "nexus"); //$NON-NLS-1$ + + this.maven = MavenPlugin.getDefault().getMaven(); + this.indexUpdater = MavenPlugin.getDefault().getIndexUpdater(); + + this.updaterJob = new IndexUpdaterJob(this, console); + + this.workspaceIndex = new NexusIndex(this, repositoryRegistry.getWorkspaceRepository(), NexusIndex.DETAILS_MIN); + } + + private NexusIndex newLocalIndex(IRepository localRepository) { + return new NexusIndex(this, localRepository, NexusIndex.DETAILS_FULL); + } + + private ArrayList<IndexCreator> getFullCreator() { + if(fullCreators == null) { + try { + PlexusContainer container = MavenPlugin.getDefault().getPlexusContainer(); + IndexCreator min = container.lookup(IndexCreator.class, MinimalArtifactInfoIndexCreator.ID); + IndexCreator mavenPlugin = container.lookup(IndexCreator.class, MavenPluginArtifactInfoIndexCreator.ID); + IndexCreator mavenArchetype = container.lookup(IndexCreator.class, MavenArchetypeArtifactInfoIndexCreator.ID); + IndexCreator jar = container.lookup(IndexCreator.class, JarFileContentsIndexCreator.ID); + + fullCreators = new ArrayList<IndexCreator>(); + fullCreators.add(min); + fullCreators.add(jar); + fullCreators.add(mavenPlugin); + fullCreators.add(mavenArchetype); + } catch(ComponentLookupException ce) { + String msg = "Error looking up component "; + console.logError(msg + "; " + ce.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ce); + + } + } + return fullCreators; + } + + private ArrayList<IndexCreator> getMinCreator() { + if(minCreators == null) { + try { + PlexusContainer container = MavenPlugin.getDefault().getPlexusContainer(); + IndexCreator min = container.lookup(IndexCreator.class, MinimalArtifactInfoIndexCreator.ID); + IndexCreator mavenArchetype = container.lookup(IndexCreator.class, MavenArchetypeArtifactInfoIndexCreator.ID); + minCreators = new ArrayList<IndexCreator>(); + minCreators.add(min); + minCreators.add(mavenArchetype); + } catch(ComponentLookupException ce) { + String msg = "Error looking up component "; + MavenLogger.log(msg, ce); + } + + } + return minCreators; + } + + /** for Unit test */ + public IndexedArtifactFile getIndexedArtifactFile(IRepository repository, ArtifactKey gav) throws CoreException { + + try { + BooleanQuery query = new BooleanQuery(); + query.add(constructQuery(MAVEN.GROUP_ID, gav.getGroupId(), SearchType.EXACT), BooleanClause.Occur.MUST); + query.add(constructQuery(MAVEN.ARTIFACT_ID, gav.getArtifactId(), SearchType.EXACT), BooleanClause.Occur.MUST); + query.add(constructQuery(MAVEN.VERSION, gav.getVersion(), SearchType.EXACT), BooleanClause.Occur.MUST); + + if(gav.getClassifier() != null) { + query.add(constructQuery(MAVEN.CLASSIFIER, gav.getClassifier(), SearchType.EXACT), BooleanClause.Occur.MUST); + } + + synchronized(getIndexLock(repository)) { + ArtifactInfo artifactInfo = getIndexer().identify(query, Collections.singleton(getIndexingContext(repository))); + if(artifactInfo != null) { + return getIndexedArtifactFile(artifactInfo); + } + } + } catch(Exception ex) { + String msg = "Illegal artifact coordinate " + ex.getMessage(); + MavenLogger.log(msg, ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_search, ex)); + } + return null; + } + + /** for Unit test */ + public IndexedArtifactFile getIndexedArtifactFile(ArtifactInfo artifactInfo) { + String groupId = artifactInfo.groupId; + String artifactId = artifactInfo.artifactId; + String repository = artifactInfo.repository; + String version = artifactInfo.version; + String classifier = artifactInfo.classifier; + String packaging = artifactInfo.packaging; + String fname = artifactInfo.fname; + if(fname == null) { + fname = artifactId + '-' + version + + (classifier != null ? '-' + classifier : "") + (packaging != null ? ('.' + packaging) : ""); //$NON-NLS-1$ //$NON-NLS-2$ + } + + long size = artifactInfo.size; + Date date = new Date(artifactInfo.lastModified); + + int sourcesExists = artifactInfo.sourcesExists.ordinal(); + int javadocExists = artifactInfo.javadocExists.ordinal(); + + String prefix = artifactInfo.prefix; + List<String> goals = artifactInfo.goals; + + return new IndexedArtifactFile(repository, groupId, artifactId, version, packaging, classifier, fname, size, date, + sourcesExists, javadocExists, prefix, goals); + } + + public IndexedArtifactFile identify(File file) throws CoreException { + try { + ArtifactInfo artifactInfo = getIndexer().identify(file); + return artifactInfo == null ? null : getIndexedArtifactFile(artifactInfo); + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_search, ex)); + } + } + + protected IndexedArtifactFile identify(IRepository repository, File file) throws CoreException { + try { + IndexingContext context = getIndexingContext(repository); + ArtifactInfo artifactInfo = identify(file, Collections.singleton(context)); + return artifactInfo == null ? null : getIndexedArtifactFile(artifactInfo); + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_search, ex)); + } + } + + /** + * Method to construct Lucene Queries without need to actually know the structure and details (field names, analyze + * details, etc) of the underlying index. Also, using this methods makes you "future proof". Naturally, at caller + * level you can still combine these queries using BooleanQuery to suit your needs. + * + * @param field + * @param query + * @param type + * @return + */ + public Query constructQuery(Field field, SearchExpression query) { + // let the default be "scored" search + SearchType st = SearchType.SCORED; + + if(query instanceof MatchTyped) { + MatchType mt = ((MatchTyped) query).getMatchType(); + + if(MatchType.EXACT.equals(mt)) { + st = SearchType.EXACT; + } + } + + return constructQuery(field, query.getStringValue(), st); + } + + private Query constructQuery(Field field, String query, SearchType searchType) { + return getIndexer().constructQuery(field, query, searchType); + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String type) throws CoreException { + return search(null, term, type, IIndex.SEARCH_ALL); + } + + public Map<String, IndexedArtifact> search(SearchExpression term, String type, int classifier) throws CoreException { + return search(null, term, type, classifier); + } + + private void addClassifiersToQuery(BooleanQuery bq, int classifier) { + boolean includeJavaDocs = (classifier & IIndex.SEARCH_JAVADOCS) > 0; + Query tq = null; + if(!includeJavaDocs) { + tq = constructQuery(MAVEN.CLASSIFIER, "javadoc", SearchType.EXACT); //$NON-NLS-1$ + bq.add(tq, Occur.MUST_NOT); + } + boolean includeSources = (classifier & IIndex.SEARCH_SOURCES) > 0; + if(!includeSources) { + tq = constructQuery(MAVEN.CLASSIFIER, "sources", SearchType.EXACT); //$NON-NLS-1$ + bq.add(tq, Occur.MUST_NOT); + } + boolean includeTests = (classifier & IIndex.SEARCH_TESTS) > 0; + if(!includeTests) { + tq = constructQuery(MAVEN.CLASSIFIER, "tests", SearchType.EXACT); //$NON-NLS-1$ + bq.add(tq, Occur.MUST_NOT); + } + } + + /** + * @return Map<String, IndexedArtifact> + */ + protected Map<String, IndexedArtifact> search(IRepository repository, SearchExpression term, String type, + int classifier) throws CoreException { + Query query; + if(IIndex.SEARCH_GROUP.equals(type)) { + query = constructQuery(MAVEN.GROUP_ID, term); + // query = new TermQuery(new Term(ArtifactInfo.GROUP_ID, term)); + // query = new PrefixQuery(new Term(ArtifactInfo.GROUP_ID, term)); + } else if(IIndex.SEARCH_ARTIFACT.equals(type)) { + BooleanQuery bq = new BooleanQuery(); + bq.add(constructQuery(MAVEN.GROUP_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add(constructQuery(MAVEN.ARTIFACT_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add( + constructQuery(MAVEN.SHA1, term.getStringValue(), term.getStringValue().length() == 40 ? SearchType.EXACT + : SearchType.SCORED), Occur.SHOULD); + addClassifiersToQuery(bq, classifier); + query = bq; + + } else if(IIndex.SEARCH_PARENTS.equals(type)) { + if(term == null) { //$NON-NLS-1$ //$NON-NLS-2$ + query = constructQuery(MAVEN.PACKAGING, "pom", SearchType.EXACT); //$NON-NLS-1$ + } else { + BooleanQuery bq = new BooleanQuery(); + bq.add(constructQuery(MAVEN.GROUP_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add(constructQuery(MAVEN.ARTIFACT_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add( + constructQuery(MAVEN.SHA1, term.getStringValue(), term.getStringValue().length() == 40 ? SearchType.EXACT + : SearchType.SCORED), Occur.SHOULD); + Query tq = constructQuery(MAVEN.PACKAGING, "pom", SearchType.EXACT); //$NON-NLS-1$ + query = new FilteredQuery(tq, new QueryWrapperFilter(bq)); + } + + } else if(IIndex.SEARCH_PLUGIN.equals(type)) { + if(term == null) { //$NON-NLS-1$ + query = constructQuery(MAVEN.PACKAGING, "maven-plugin", SearchType.EXACT); //$NON-NLS-1$ + } else { + BooleanQuery bq = new BooleanQuery(); + bq.add(constructQuery(MAVEN.GROUP_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add(constructQuery(MAVEN.ARTIFACT_ID, term), Occur.SHOULD); //$NON-NLS-1$ + Query tq = constructQuery(MAVEN.PACKAGING, "maven-plugin", SearchType.EXACT); //$NON-NLS-1$ + query = new FilteredQuery(tq, new QueryWrapperFilter(bq)); + } + + } else if(IIndex.SEARCH_ARCHETYPE.equals(type)) { + BooleanQuery bq = new BooleanQuery(); + bq.add(constructQuery(MAVEN.GROUP_ID, term), Occur.SHOULD); //$NON-NLS-1$ + bq.add(constructQuery(MAVEN.ARTIFACT_ID, term), Occur.SHOULD); //$NON-NLS-1$ + Query tq = constructQuery(MAVEN.PACKAGING, "maven-archetype", SearchType.EXACT); //$NON-NLS-1$ + query = new FilteredQuery(tq, new QueryWrapperFilter(bq)); + + } else if(IIndex.SEARCH_PACKAGING.equals(type)) { + query = constructQuery(MAVEN.PACKAGING, term); + } else if(IIndex.SEARCH_SHA1.equals(type)) { + // if hash is 40 chars, it is "complete", otherwise assume prefix + query = constructQuery(MAVEN.SHA1, term.getStringValue(), term.getStringValue().length() == 40 ? SearchType.EXACT + : SearchType.SCORED); + } else { + return Collections.emptyMap(); + } + + Map<String, IndexedArtifact> result = new TreeMap<String, IndexedArtifact>(); + + try { + IteratorSearchResponse response; + + synchronized(getIndexLock(repository)) { + IndexingContext context = getIndexingContext(repository); + if(context == null) { + response = getIndexer().searchIterator(new IteratorSearchRequest(query)); + } else { + response = getIndexer().searchIterator(new IteratorSearchRequest(query, context)); + } + + for(ArtifactInfo artifactInfo : response.getResults()) { + addArtifactFile(result, getIndexedArtifactFile(artifactInfo), null, null, artifactInfo.packaging); + } + + // https://issues.sonatype.org/browse/MNGECLIPSE-1630 + // lucene can't handle prefix queries that match many index entries. + // to workaround, use term query to locate group artifacts and manually + // match subgroups + if(IIndex.SEARCH_GROUP.equals(type) && context != null) { + Set<String> groups = context.getAllGroups(); + for(String group : groups) { + if(term == null || group.startsWith(term.getStringValue()) && !group.equals(term.getStringValue())) { + String key = getArtifactFileKey(group, group, null, null); + result.put(key, new IndexedArtifact(group, group, null, null, null)); + } + } + } + } + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_search, ex)); + } + + return result; + } + + /** + * @return Map<String, IndexedArtifact> + */ + protected Map<String, IndexedArtifact> search(IRepository repository, SearchExpression term, String type) + throws CoreException { + return search(repository, term, type, IIndex.SEARCH_ALL); + } + + /** + * @return Map<String, IndexedArtifact> + */ + protected Map<String, IndexedArtifact> search(IRepository repository, Query query) throws CoreException { + Map<String, IndexedArtifact> result = new TreeMap<String, IndexedArtifact>(); + try { + IteratorSearchResponse response; + + synchronized(getIndexLock(repository)) { + IndexingContext context = getIndexingContext(repository); + if(context == null) { + response = getIndexer().searchIterator(new IteratorSearchRequest(query)); + } else { + response = getIndexer().searchIterator(new IteratorSearchRequest(query, context)); + } + } + + for(ArtifactInfo artifactInfo : response.getResults()) { + IndexedArtifactFile af = getIndexedArtifactFile(artifactInfo); + addArtifactFile(result, af, null, null, artifactInfo.packaging); + } + + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_search, ex)); + } + return result; + } + + private void addArtifactFile(Map<String, IndexedArtifact> result, IndexedArtifactFile af, String className, + String packageName, String packaging) { + String group = af.group; + String artifact = af.artifact; + String key = getArtifactFileKey(group, artifact, packageName, className); + IndexedArtifact indexedArtifact = result.get(key); + if(indexedArtifact == null) { + indexedArtifact = new IndexedArtifact(group, artifact, packageName, className, packaging); + result.put(key, indexedArtifact); + } + indexedArtifact.addFile(af); + } + + protected String getArtifactFileKey(String group, String artifact, String packageName, String className) { + return className + " : " + packageName + " : " + group + " : " + artifact; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + private void purgeCurrentIndex(IndexingContext context) throws IOException { + context.purge(); + } + + private void reindexLocalRepository(IRepository repository, boolean force, final IProgressMonitor monitor) + throws CoreException { + try { + fireIndexUpdating(repository); + //IndexInfo indexInfo = getIndexInfo(indexName); + IndexingContext context = getIndexingContext(repository); + if(force) { + purgeCurrentIndex(context); + } + if(context.getRepository().isDirectory()) { + getIndexer().scan(context, new ArtifactScanningMonitor(context.getRepository(), monitor, console), false); + } + fireIndexChanged(repository); + console.logMessage("Updated local repository index"); + } catch(Exception ex) { + MavenLogger.log("Unable to re-index " + repository.toString(), ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_reindexing, ex)); + } finally { + fireIndexChanged(repository); + } + } + + private void reindexWorkspace(boolean force, IProgressMonitor monitor) throws CoreException { + IRepository workspaceRepository = repositoryRegistry.getWorkspaceRepository(); + try { + IndexingContext context = getIndexingContext(workspaceRepository); + if(force) { + purgeCurrentIndex(context); + } + for(IMavenProjectFacade facade : projectManager.getProjects()) { + addDocument(workspaceRepository, facade.getPomFile(), // + facade.getArtifactKey()); + } + } catch(Exception ex) { + MavenLogger.log("Unable to re-index " + workspaceRepository.toString(), ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_reindexing, ex)); + } finally { + fireIndexChanged(workspaceRepository); + } + } + + protected void addDocument(IRepository repository, File file, ArtifactKey key) { + synchronized(getIndexLock(repository)) { + IndexingContext context = getIndexingContext(repository); + if(context == null) { + // TODO log + return; + } + try { + ArtifactContext artifactContext; + if(repository.isScope(IRepositoryRegistry.SCOPE_WORKSPACE)) { + IMavenProjectFacade facade = getProjectByArtifactKey(key); + artifactContext = getWorkspaceArtifactContext(facade, context); + } else { + artifactContext = getArtifactContext(file, context); + } + getIndexer().addArtifactToIndex(artifactContext, context); + } catch(Exception ex) { + String msg = "Unable to add " + getDocumentKey(key); + console.logError(msg + "; " + ex.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + } + } + } + + private IMavenProjectFacade getProjectByArtifactKey(ArtifactKey artifactKey) throws CoreException { + for(IMavenProjectFacade facade : projectManager.getProjects()) { + if(facade.getArtifactKey().equals(artifactKey)) { + return facade; + } + } + + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_unexpected, new IllegalArgumentException(String.format( + "Workspace project with key %s not found!", new Object[] {artifactKey})))); //$NON-NLS-1$ + } + + protected void removeDocument(IRepository repository, File file, ArtifactKey key, IMavenProjectFacade facade) { + synchronized(getIndexLock(repository)) { + try { + IndexingContext context = getIndexingContext(repository); + if(context == null) { + String msg = "Unable to find document to remove" + getDocumentKey(key); + MavenLogger.log(new Status(IStatus.ERROR, "org.eclipse.m2e", msg)); //$NON-NLS-1$ + return; + } + ArtifactContext artifactContext; + if(repository.isScope(IRepositoryRegistry.SCOPE_WORKSPACE)) { + if(facade == null) { + // try to get one, but you MUST have facade in case of project deletion, see mavenProjectChanged() + facade = getProjectByArtifactKey(key); + } + artifactContext = getWorkspaceArtifactContext(facade, context); + } else { + artifactContext = getArtifactContext(file, context); + } + getIndexer().deleteArtifactFromIndex(artifactContext, context); + } catch(Exception ex) { + String msg = "Unable to remove " + getDocumentKey(key); + console.logError(msg + "; " + ex.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + } + } + + fireIndexChanged(repository); + } + + private ArtifactContext getArtifactContext(File file, IndexingContext context) + throws IllegalArtifactCoordinateException { + return getArtifactContextProducer().getArtifactContext(context, file); + } + + private ArtifactContext getWorkspaceArtifactContext(IMavenProjectFacade facade, IndexingContext context) + throws CoreException { + IRepository workspaceRepository = repositoryRegistry.getWorkspaceRepository(); + ArtifactKey key = facade.getArtifactKey(); + ArtifactInfo ai = new ArtifactInfo(workspaceRepository.getUid(), key.getGroupId(), key.getArtifactId(), + key.getVersion(), null); + ai.packaging = facade.getPackaging(); + File pomFile = facade.getPomFile(); + File artifactFile = (pomFile != null) ? pomFile.getParentFile() : null; + try { + Gav gav = new Gav(key.getGroupId(), key.getArtifactId(), key.getVersion()); + return new ArtifactContext(pomFile, artifactFile, null, ai, gav); + } catch(IllegalArtifactCoordinateException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_unexpected, ex)); + } + } + + protected void scheduleIndexUpdate(final IRepository repository, final boolean force) { + if(repository != null) { + IndexCommand command = new IndexUpdaterJob.IndexCommand() { + public void run(IProgressMonitor monitor) throws CoreException { + updateIndex(repository, force, monitor); + } + }; + updaterJob.addCommand(command); + updaterJob.schedule(1000L); + } + } + + protected Set<String> getRootGroups(IRepository repository) throws CoreException { + synchronized(getIndexLock(repository)) { + IndexingContext context = getIndexingContext(repository); + if(context != null) { + try { + Set<String> rootGroups = context.getRootGroups(); + return rootGroups; + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, // + NLS.bind(Messages.NexusIndexManager_error_root_grp, repository.toString()), ex)); + } + } + return Collections.emptySet(); + } + } + + /** for unit tests */ + public IndexedArtifactGroup[] getRootIndexedArtifactGroups(IRepository repository) throws CoreException { + synchronized(getIndexLock(repository)) { + IndexingContext context = getIndexingContext(repository); + if(context != null) { + try { + Set<String> rootGroups = context.getRootGroups(); + IndexedArtifactGroup[] groups = new IndexedArtifactGroup[rootGroups.size()]; + int i = 0; + for(String group : rootGroups) { + groups[i++ ] = new IndexedArtifactGroup(repository, group); + } + return groups; + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, // + NLS.bind(Messages.NexusIndexManager_error_root_grp, repository.toString()), ex)); + } + } + return new IndexedArtifactGroup[0]; + } + } + + /** public for unit tests only! */ + public IndexingContext getIndexingContext(IRepository repository) { + return repository == null ? null : getIndexer().getIndexingContexts().get(repository.getUid()); + } + + private NexusIndexer getIndexer() { + synchronized(indexerLock) { + if(indexer == null) { + indexer = MavenPlugin.getDefault().getNexusIndexer(); + } + } + return indexer; + } + + private ArtifactContextProducer getArtifactContextProducer() { + synchronized(contextProducerLock) { + if(artifactContextProducer == null) { + artifactContextProducer = MavenPlugin.getDefault().getArtifactContextProducer(); + } + } + return artifactContextProducer; + } + + public static String getDocumentKey(ArtifactKey artifact) { + String groupId = artifact.getGroupId(); + if(groupId == null) { + groupId = Messages.NexusIndexManager_inherited; + } + + String artifactId = artifact.getArtifactId(); + + String version = artifact.getVersion(); + if(version == null) { + version = Messages.NexusIndexManager_inherited; + } + + String key = groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + "-" + version; //$NON-NLS-1$ + + String classifier = artifact.getClassifier(); + if(classifier != null) { + key += "-" + classifier; //$NON-NLS-1$ + } + + // TODO use artifact handler to retrieve extension + // cstamas: will not work since ArtifactKey misses type + // either get packaging from POM or store/honor extension + return key + ".pom"; //$NON-NLS-1$ + } + + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + /* + * This method is called while holding workspace lock. Avoid long-running operations if possible. + */ + + synchronized(getIndexLock(repositoryRegistry.getWorkspaceRepository())) { + IndexingContext context = getIndexingContext(repositoryRegistry.getWorkspaceRepository()); + + if(context != null) { + // workspace indexing context can by null during startup due to MNGECLIPSE-1633 + for(MavenProjectChangedEvent event : events) { + IMavenProjectFacade oldFacade = event.getOldMavenProject(); + if(oldFacade != null) { + removeDocument(repositoryRegistry.getWorkspaceRepository(), oldFacade.getPomFile(), + oldFacade.getArtifactKey(), oldFacade); + fireIndexRemoved(repositoryRegistry.getWorkspaceRepository()); + } + IMavenProjectFacade facade = event.getMavenProject(); + if(facade != null) { + addDocument(repositoryRegistry.getWorkspaceRepository(), facade.getPomFile(), facade.getArtifactKey()); + fireIndexAdded(repositoryRegistry.getWorkspaceRepository()); + } + } + } + } + } + + public NexusIndex getWorkspaceIndex() { + return workspaceIndex; + } + + public NexusIndex getLocalIndex() { + IRepository localRepository = repositoryRegistry.getLocalRepository(); + synchronized(getIndexLock(localRepository)) { + if(localIndex == null) { + localIndex = newLocalIndex(localRepository); + } + } + return localIndex; + } + + public IIndex getIndex(IProject project) { + IMavenProjectFacade projectFacade = project != null ? projectManager.getProject(project) : null; + + ArrayList<IIndex> indexes = new ArrayList<IIndex>(); + indexes.add(getWorkspaceIndex()); + indexes.add(getLocalIndex()); + + if(projectFacade != null) { + LinkedHashSet<ArtifactRepositoryRef> repositories = new LinkedHashSet<ArtifactRepositoryRef>(); + repositories.addAll(projectFacade.getArtifactRepositoryRefs()); + repositories.addAll(projectFacade.getPluginArtifactRepositoryRefs()); + + for(ArtifactRepositoryRef repositoryRef : repositories) { + IRepository repository = repositoryRegistry.getRepository(repositoryRef); + indexes.add(getIndex(repository)); + } + } else { + for(IRepository repository : repositoryRegistry.getRepositories(IRepositoryRegistry.SCOPE_SETTINGS)) { + indexes.add(getIndex(repository)); + } + } + + return new CompositeIndex(indexes); + } + + public IIndex getAllIndexes() { + ArrayList<IIndex> indexes = new ArrayList<IIndex>(); + indexes.add(getWorkspaceIndex()); + indexes.add(getLocalIndex()); + + for(IMavenProjectFacade facade : projectManager.getProjects()) { + LinkedHashSet<ArtifactRepositoryRef> repositories = new LinkedHashSet<ArtifactRepositoryRef>(); + repositories.addAll(facade.getArtifactRepositoryRefs()); + repositories.addAll(facade.getPluginArtifactRepositoryRefs()); + + for(ArtifactRepositoryRef repositoryRef : repositories) { + IRepository repository = repositoryRegistry.getRepository(repositoryRef); + indexes.add(getIndex(repository)); + } + } + + return new CompositeIndex(indexes); + } + + public NexusIndex getIndex(IRepository repository) { + String details = getIndexDetails(repository); + return new NexusIndex(this, repository, details); + } + + protected File getIndexDirectoryFile(IRepository repository) { + return new File(baseIndexDir, repository.getUid()); + } + + protected Directory getIndexDirectory(IRepository repository) throws IOException { + return FSDirectory.getDirectory(getIndexDirectoryFile(repository)); + } + + public IndexedArtifactGroup resolveGroup(IndexedArtifactGroup group) { + IRepository repository = group.getRepository(); + String prefix = group.getPrefix(); + try { + IndexedArtifactGroup g = new IndexedArtifactGroup(repository, prefix); + for(IndexedArtifact a : search(repository, new SourcedSearchExpression(prefix), IIndex.SEARCH_GROUP).values()) { + String groupId = a.getGroupId(); + if(groupId.equals(prefix)) { + g.getFiles().put(a.getArtifactId(), a); + } else if(groupId.startsWith(prefix + ".")) { //$NON-NLS-1$ + int start = prefix.length() + 1; + int end = groupId.indexOf('.', start); + String key = end > -1 ? groupId.substring(0, end) : groupId; + g.getNodes().put(key, new IndexedArtifactGroup(repository, key)); + } + } + + return g; + + } catch(CoreException ex) { + MavenLogger.log("Can't retrieve groups for " + repository.toString() + ":" + prefix, ex); //$NON-NLS-2$ + return group; + } + } + + public void repositoryAdded(IRepository repository, IProgressMonitor monitor) throws CoreException { + String details = getIndexDetails(repository); + + // for consistency, always process indexes using our background thread + setIndexDetails(repository, null, details, null/*async*/); + } + + /** For tests only */ + public String getIndexDetails(IRepository repository) { + String details = indexDetails.getProperty(repository.getUid()); + + if(details == null) { + if(repository.isScope(IRepositoryRegistry.SCOPE_SETTINGS) && repository.getMirrorId() == null) { + details = NexusIndex.DETAILS_MIN; + } else if(repository.isScope(IRepositoryRegistry.SCOPE_LOCAL)) { + details = NexusIndex.DETAILS_MIN; + } else if(repository.isScope(IRepositoryRegistry.SCOPE_WORKSPACE)) { + details = NexusIndex.DETAILS_MIN; + } else { + details = NexusIndex.DETAILS_DISABLED; + } + } + + return details; + } + + /** + * Updates index synchronously if monitor!=null. Schedules index update otherwise. ... and yes, I know this ain't + * kosher. Public for unit tests only! + */ + public void setIndexDetails(IRepository repository, String details, IProgressMonitor monitor) throws CoreException { + setIndexDetails(repository, details, details, monitor); + } + + private void setIndexDetails(IRepository repository, String details, String defaultDetails, IProgressMonitor monitor) + throws CoreException { + if(details != null) { + indexDetails.setProperty(repository.getUid(), details); + + writeIndexDetails(); + } else { + details = defaultDetails; + } + + synchronized(getIndexLock(repository)) { + IndexingContext indexingContext = getIndexingContext(repository); + + try { + if(NexusIndex.DETAILS_DISABLED.equals(details)) { + if(indexingContext != null) { + getIndexer().removeIndexingContext(indexingContext, false /*removeFiles*/); + fireIndexRemoved(repository); + } + } else { + if(indexingContext != null) { + getIndexer().removeIndexingContext(indexingContext, false); + } + + createIndexingContext(repository, details); + + fireIndexAdded(repository); + + if(monitor != null) { + updateIndex(repository, false, monitor); + } else { + scheduleIndexUpdate(repository, false); + } + } + } catch(IOException ex) { + String msg = "Error changing index details " + repository.toString(); + console.logError(msg + "; " + ex.getMessage()); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_add_repo, ex)); + } + + if(repository.isScope(IRepositoryRegistry.SCOPE_LOCAL)) { + // note that we are still synchronized on repository lock at this point + this.localIndex = newLocalIndex(repositoryRegistry.getLocalRepository()); + } + } + } + + protected IndexingContext createIndexingContext(IRepository repository, String details) throws IOException { + IndexingContext indexingContext; + Directory directory = getIndexDirectory(repository); + + File repositoryPath = null; + if(repository.getBasedir() != null) { + repositoryPath = repository.getBasedir().getCanonicalFile(); + } + + ArrayList<IndexCreator> indexers = getIndexers(details); + + indexingContext = getIndexer().addIndexingContextForced(repository.getUid(), // + repository.getUrl(), // + repositoryPath, // + directory, // + repository.getUrl(), null, // + indexers); + + indexingContext.setSearchable(false); + + return indexingContext; + } + + protected ArrayList<IndexCreator> getIndexers(String details) { + boolean fullIndex = NexusIndex.DETAILS_FULL.equals(details); + ArrayList<IndexCreator> indexers = fullIndex ? getFullCreator() : getMinCreator(); + return indexers; + } + + public void repositoryRemoved(IRepository repository, IProgressMonitor monitor) { + synchronized(getIndexLock(repository)) { + try { + IndexingContext context = getIndexingContext(repository); + if(context == null) { + return; + } + getIndexer().removeIndexingContext(context, false); + } catch(IOException ie) { + String msg = "Unable to delete files for index"; + MavenLogger.log(msg, ie); + } + } + + fireIndexRemoved(repository); + } + + protected void fireIndexAdded(IRepository repository) { + synchronized(indexListeners) { + for(IndexListener listener : indexListeners) { + listener.indexAdded(repository); + } + } + } + + protected void fireIndexRemoved(IRepository repository) { + synchronized(updatingIndexes) { + if(repository != null) { + //since workspace index can be null at startup, guard against nulls + updatingIndexes.remove(repository.getUid()); + } + } + synchronized(indexListeners) { + for(IndexListener listener : indexListeners) { + listener.indexRemoved(repository); + } + } + } + + protected boolean isUpdatingIndex(IRepository repository) { + synchronized(updatingIndexes) { + return updatingIndexes.contains(repository.getUid()); + } + } + + protected void fireIndexUpdating(IRepository repository) { + synchronized(updatingIndexes) { + if(repository != null) { + //since workspace index can be null at startup, guard against nulls + updatingIndexes.add(repository.getUid()); + } + } + synchronized(indexListeners) { + for(IndexListener listener : indexListeners) { + listener.indexUpdating(repository); + } + } + } + + protected void fireIndexChanged(IRepository repository) { + synchronized(updatingIndexes) { + if(repository != null) { + //since workspace index can be null at startup, guard against nulls + updatingIndexes.remove(repository.getUid()); + } + + } + synchronized(indexListeners) { + for(IndexListener listener : indexListeners) { + listener.indexChanged(repository); + } + } + } + + public void removeIndexListener(IndexListener listener) { + synchronized(indexListeners) { + indexListeners.remove(listener); + } + } + + public void addIndexListener(IndexListener listener) { + synchronized(indexListeners) { + if(!indexListeners.contains(listener)) { + indexListeners.add(listener); + } + } + } + + //Public for testing purpose. + public void updateIndex(IRepository repository, boolean force, IProgressMonitor monitor) throws CoreException { + synchronized(getIndexLock(repository)) { + if(repository.isScope(IRepositoryRegistry.SCOPE_WORKSPACE)) { + reindexWorkspace(force, monitor); + } else { + IndexingContext context = getIndexingContext(repository); + if(context != null) { + if(context.getRepository() != null) { + reindexLocalRepository(repository, force, monitor); + } else { + if(!force) { + //if 'force' is not set, then only do the remote update if this value is set + IMavenConfiguration mavenConfig = MavenPlugin.getDefault().getMavenConfiguration(); + if(mavenConfig.isUpdateIndexesOnStartup()) { + updateRemoteIndex(repository, force, monitor); + } + } else { + updateRemoteIndex(repository, force, monitor); + } + } + } + } + IndexingContext context = getIndexingContext(repository); + if(context != null) { + context.setSearchable(true); + } + } + } + + /* + * Callers must hold repository access synchronisation lock + */ + private void updateRemoteIndex(IRepository repository, boolean force, IProgressMonitor monitor) { + if(repository == null) { + return; + } + + if(monitor != null) { + monitor.setTaskName(NLS.bind(Messages.NexusIndexManager_task_updating, repository.toString())); + } + console.logMessage("Updating index " + repository.toString()); + try { + fireIndexUpdating(repository); + + IndexingContext context = getIndexingContext(repository); + + if(context != null) { + IndexUpdateRequest request = newIndexUpdateRequest(repository, context, monitor); + request.setForceFullUpdate(force); + + Lock cacheLock = locker.lock(request.getLocalIndexCacheDir()); + try { + boolean updated; + + request.setCacheOnly(true); + IndexUpdateResult result = indexUpdater.fetchAndUpdateIndex(request); + if(result.isFullUpdate() || !context.isSearchable()) { + // need to fully recreate index + + // 1. process index gz into cached/shared lucene index. this can be a noop if cache is uptodate + String details = getIndexDetails(repository); + String id = repository.getUid() + "-cache"; //$NON-NLS-1$ + File luceneCache = new File(request.getLocalIndexCacheDir(), details); + Directory directory = FSDirectory.getDirectory(luceneCache); + IndexingContext cacheCtx = getIndexer().addIndexingContextForced(id, id, null, directory, null, null, + getIndexers(details)); + request = newIndexUpdateRequest(repository, cacheCtx, monitor); + request.setOffline(true); + indexUpdater.fetchAndUpdateIndex(request); + + // 2. copy cached/shared (this is not very elegant, oh well) + getIndexer().removeIndexingContext(context, true); // nuke workspace index files + getIndexer().removeIndexingContext(cacheCtx, false); // keep the cache! + FileUtils.cleanDirectory(context.getIndexDirectoryFile()); + FileUtils.copyDirectory(luceneCache, context.getIndexDirectoryFile()); // copy cached lucene index + context = createIndexingContext(repository, details); // re-create indexing context + + updated = true; + } else { + // incremental change + request = newIndexUpdateRequest(repository, context, monitor); + request.setOffline(true); // local cache is already uptodate, no need to + result = indexUpdater.fetchAndUpdateIndex(request); + updated = result.getTimestamp() != null; + } + + if(updated) { + console.logMessage("Updated index for " + repository.toString()); + } else { + console.logMessage("No index update available for " + repository.toString()); + } + + } finally { + cacheLock.release(); + } + } + } catch(FileNotFoundException e) { + String msg = "Unable to update index for " + repository.toString() + ": " + e.getMessage(); //$NON-NLS-2$ + console.logError(msg); + } catch(Exception ie) { + String msg = "Unable to update index for " + repository.toString(); + MavenLogger.log(msg, ie); + console.logError(msg); + } finally { + fireIndexChanged(repository); + } + } + + protected IndexUpdateRequest newIndexUpdateRequest(IRepository repository, IndexingContext context, + IProgressMonitor monitor) throws IOException, CoreException { + ProxyInfo proxyInfo = maven.getProxyInfo(repository.getProtocol()); + AuthenticationInfo authenticationInfo = repository.getAuthenticationInfo(); + + IndexUpdateRequest request = new IndexUpdateRequest(context, new AsyncFetcher(authenticationInfo, proxyInfo, + monitor)); + File localRepo = repositoryRegistry.getLocalRepository().getBasedir(); + File indexCacheBasedir = new File(localRepo, ".cache/m2e/" + MavenPlugin.getVersion()).getCanonicalFile(); //$NON-NLS-1$ + File indexCacheDir = new File(indexCacheBasedir, repository.getUid()); + indexCacheDir.mkdirs(); + request.setLocalIndexCacheDir(indexCacheDir); + return request; + } + + public void initialize(IProgressMonitor monitor) throws CoreException { + try { + BufferedInputStream is = new BufferedInputStream(new FileInputStream(getIndexDetailsFile())); + try { + indexDetails.load(is); + } finally { + is.close(); + } + } catch(FileNotFoundException e) { + // that's quite alright + } catch(IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_read_index, e)); + } + } + + protected void writeIndexDetails() throws CoreException { + try { + File indexDetailsFile = getIndexDetailsFile(); + indexDetailsFile.getParentFile().mkdirs(); + BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(indexDetailsFile)); + try { + indexDetails.store(os, null); + } finally { + os.close(); + } + } catch(IOException e) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.NexusIndexManager_error_write_index, e)); + } + } + + private File getIndexDetailsFile() { + return new File(baseIndexDir, "indexDetails.properties"); //$NON-NLS-1$ + } + + /** for unit tests only */ + public Job getIndexUpdateJob() { + return updaterJob; + } + + public String getIndexerId() { + return Messages.NexusIndexManager_78; + } + + private Object getIndexLock(IRepository repository) { + if(repository == null) { + return new Object(); + } + // NOTE: We ultimately want to prevent concurrent access to the IndexingContext so we sync on the repo UID and not on the repo instance. + synchronized(indexLocks) { + Object lock = indexLocks.get(repository.getUid()); + if(lock == null) { + lock = new Object(); + indexLocks.put(repository.getUid(), lock); + } + return lock; + } + } + + /// REMOVE THIS BELOW ONCE Maven Indexer upgraded to 3.2.0-SNAPSHOT + /// In that moment this code becomes duplicated and already in place, this method added + + protected ArtifactInfo identify(File artifact, Collection<IndexingContext> contexts) throws IOException { + + FileInputStream is = null; + + try { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + + is = new FileInputStream(artifact); + + byte[] buff = new byte[4096]; + + int n; + + while((n = is.read(buff)) > -1) { + sha1.update(buff, 0, n); + } + + byte[] digest = sha1.digest(); + + Query q = getIndexer().constructQuery(MAVEN.SHA1, encode(digest), SearchType.EXACT); + + return getIndexer().identify(q, contexts); + + } catch(NoSuchAlgorithmException ex) { + throw new IOException("Unable to calculate digest"); + } finally { + IOUtil.close(is); + } + + } + + private static final char[] DIGITS = "0123456789abcdef".toCharArray(); + + private static String encode(byte[] digest) { + char[] buff = new char[digest.length * 2]; + + int n = 0; + + for(byte b : digest) { + buff[n++ ] = DIGITS[(0xF0 & b) >> 4]; + buff[n++ ] = DIGITS[0x0F & b]; + } + + return new String(buff); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecycle/LifecycleMappingFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecycle/LifecycleMappingFactory.java new file mode 100644 index 00000000..78c43fe7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecycle/LifecycleMappingFactory.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.lifecycle; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.project.IgnoreMojoProjectConfiguration; +import org.eclipse.m2e.core.internal.project.MojoExecutionProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.CustomizableLifecycleMapping; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.project.configurator.PluginExecutionFilter; + + +/** + * LifecycleMappingFactory + * + * @author igor + */ +public class LifecycleMappingFactory { + + public static final String EXTENSION_LIFECYCLE_MAPPINGS = IMavenConstants.PLUGIN_ID + ".lifecycleMappings"; //$NON-NLS-1$ + + public static final String EXTENSION_PROJECT_CONFIGURATORS = IMavenConstants.PLUGIN_ID + ".projectConfigurators"; //$NON-NLS-1$ + + private static final String ELEMENT_LIFECYCLE_MAPPING = "lifecycleMapping"; //$NON-NLS-1$ + + private static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + + private static final String ATTR_PACKAGING_TYPE = "packaging-type"; //$NON-NLS-1$ + + private static final String ATTR_ID = "id"; //$NON-NLS-1$ + + private static final String ELEMENT_CONFIGURATOR = "configurator"; //$NON-NLS-1$ + + private static final String ELEMENT_MOJO = "mojo"; //$NON-NLS-1$ + + private static final String ELEMENT_IGNORE = "ignore"; //$NON-NLS-1$ + + private static final String ELEMENT_EXECUTE = "execute"; + + private static final String ATTR_GROUPID = "groupId"; + + private static final String ATTR_ARTIFACTID = "artifactId"; + + private static final String ATTR_VERSIONRANGE = "versionRange"; + + private static final String ATTR_GOALS = "goals"; + + /** + * Returns default lifecycle mapping for specified packaging type or null if no such lifecycle mapping + */ + public static ILifecycleMapping getLifecycleMappingFor(String packagingType) { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint configuratorsExtensionPoint = registry.getExtensionPoint(EXTENSION_LIFECYCLE_MAPPINGS); + if(configuratorsExtensionPoint != null) { + IExtension[] configuratorExtensions = configuratorsExtensionPoint.getExtensions(); + for(IExtension extension : configuratorExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_LIFECYCLE_MAPPING)) { + if(packagingType.equals(element.getAttribute(ATTR_PACKAGING_TYPE))) { + return createLifecycleMapping(element); + } + } + } + } + } + return null; + } + + protected static ILifecycleMapping createLifecycleMapping(IConfigurationElement element) { + try { + ILifecycleMapping mapping = (ILifecycleMapping) element.createExecutableExtension(ATTR_CLASS); + if(mapping instanceof CustomizableLifecycleMapping) { + CustomizableLifecycleMapping customizable = (CustomizableLifecycleMapping) mapping; + for(IConfigurationElement mojo : element.getChildren(ELEMENT_MOJO)) { + AbstractProjectConfigurator configurator = null; + if(mojo.getChildren(ELEMENT_IGNORE).length > 0) { + configurator = new IgnoreMojoProjectConfiguration(); + } else if(mojo.getChildren(ELEMENT_EXECUTE).length > 0) { + configurator = createMojoExecution(mojo.getChildren(ELEMENT_EXECUTE)[0]); + } else if(mojo.getChildren(ELEMENT_CONFIGURATOR).length > 0) { + String configuratorId = mojo.getChildren(ELEMENT_CONFIGURATOR)[0].getAttribute(ATTR_ID); + configurator = createProjectConfigurator(configuratorId, true/*bare*/); + } else { + MavenLogger.log("Invalid lifecycle mapping configuration element: " + mojo.toString()); + } + if(configurator != null) { + configurator.addPluginExecutionFilter(createPluginExecutionFilter(mojo)); + customizable.addConfigurator(configurator); + } + } + } + return mapping; + } catch(CoreException ex) { + MavenLogger.log(ex); + } + return null; + } + + private static AbstractProjectConfigurator createMojoExecution(IConfigurationElement configuration) { + boolean runOnIncremental = true; // TODO + return new MojoExecutionProjectConfigurator(runOnIncremental); + } + + private static PluginExecutionFilter createPluginExecutionFilter(IConfigurationElement configuration) { + String groupId = configuration.getAttribute(ATTR_GROUPID); + String artifactId = configuration.getAttribute(ATTR_ARTIFACTID); + String versionRange = configuration.getAttribute(ATTR_VERSIONRANGE); + String goals = configuration.getAttribute(ATTR_GOALS); + return new PluginExecutionFilter(groupId, artifactId, versionRange, goals); + } + + public static ILifecycleMapping getLifecycleMapping(String mappingId) { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint configuratorsExtensionPoint = registry.getExtensionPoint(EXTENSION_LIFECYCLE_MAPPINGS); + if(configuratorsExtensionPoint != null) { + IExtension[] configuratorExtensions = configuratorsExtensionPoint.getExtensions(); + for(IExtension extension : configuratorExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_LIFECYCLE_MAPPING)) { + if(mappingId.equals(element.getAttribute(ATTR_ID))) + return createLifecycleMapping(element); + } + } + } + } + return null; + } + + public static AbstractProjectConfigurator getProjectConfigurator(String configuratorId) { + return createProjectConfigurator(configuratorId, false/*bare*/); + } + + protected static AbstractProjectConfigurator createProjectConfigurator(String configuratorId, boolean bare) { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint configuratorsExtensionPoint = registry.getExtensionPoint(EXTENSION_PROJECT_CONFIGURATORS); + if(configuratorsExtensionPoint != null) { + IExtension[] configuratorExtensions = configuratorsExtensionPoint.getExtensions(); + for(IExtension extension : configuratorExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_CONFIGURATOR)) { + if(configuratorId.equals(element.getAttribute(AbstractProjectConfigurator.ATTR_ID))) { + try { + AbstractProjectConfigurator configurator = (AbstractProjectConfigurator) element + .createExecutableExtension(AbstractProjectConfigurator.ATTR_CLASS); + + MavenPlugin plugin = MavenPlugin.getDefault(); + configurator.setProjectManager(plugin.getMavenProjectManager()); + configurator.setMavenConfiguration(plugin.getMavenConfiguration()); + configurator.setMarkerManager(plugin.getMavenMarkerManager()); + configurator.setConsole(plugin.getConsole()); + + if(!bare) { + for(IConfigurationElement mojo : element.getChildren(ELEMENT_MOJO)) { + configurator.addPluginExecutionFilter(createPluginExecutionFilter(mojo)); + } + } + + return configurator; + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + } + return null; + } + + public static AbstractProjectConfigurator createProjectConfiguratorFor(MojoExecution execution) { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint configuratorsExtensionPoint = registry.getExtensionPoint(EXTENSION_PROJECT_CONFIGURATORS); + if(configuratorsExtensionPoint != null) { + IExtension[] configuratorExtensions = configuratorsExtensionPoint.getExtensions(); + for(IExtension extension : configuratorExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_CONFIGURATOR)) { + if(isConfiguratorEnabledFor(element, execution)) { + try { + AbstractProjectConfigurator configurator = (AbstractProjectConfigurator) element + .createExecutableExtension(AbstractProjectConfigurator.ATTR_CLASS); + + MavenPlugin plugin = MavenPlugin.getDefault(); + configurator.setProjectManager(plugin.getMavenProjectManager()); + configurator.setMavenConfiguration(plugin.getMavenConfiguration()); + configurator.setMarkerManager(plugin.getMavenMarkerManager()); + configurator.setConsole(plugin.getConsole()); + + for(IConfigurationElement mojo : element.getChildren(ELEMENT_MOJO)) { + configurator.addPluginExecutionFilter(createPluginExecutionFilter(mojo)); + } + + return configurator; + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + } + return null; + } + + private static boolean isConfiguratorEnabledFor(IConfigurationElement configuration, MojoExecution execution) { + for(IConfigurationElement mojo : configuration.getChildren(ELEMENT_MOJO)) { + if(createPluginExecutionFilter(mojo).match(execution)) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties new file mode 100644 index 00000000..4c740352 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/messages.properties @@ -0,0 +1,498 @@ +AbstractProjectConfigurator_error_missing_nature=Project does not have required nature +AbstractTransferListenerAdapter_4=0% {0} +AbstractTransferListenerAdapter_byte=B +AbstractTransferListenerAdapter_cancelled=Transfer is canceled +AbstractTransferListenerAdapter_kb=KB +AbstractTransferListenerAdapter_mb=MB +AbstractTransferListenerAdapter_subtask=error {0} +AddDependencyAction_error_msg=Can't add dependency to {0} +AddDependencyAction_error_title=Add Dependency +AddDependencyAction_searchDialog_title=Add Dependency +AddDependencyDialog_artifactId_label=Artifact Id: +AddDependencyDialog_groupId_label=Group Id: +AddDependencyDialog_info_label=Info: +AddDependencyDialog_info_transitive={0}-{1}-{2} is already a transitive dependency.\n +AddDependencyDialog_itemsSelected={0} items selected. +AddDependencyDialog_itemSelected={0} item selected. +AddDependencyDialog_multipleValuesSelected=(multiple values selected) +AddDependencyDialog_results_label=Search Results: +AddDependencyDialog_scope_label=Scope: +AddDependencyDialog_search_label=Enter coordinate, sha1 prefix, project name.. +AddDependencyDialog_search_message=(coordinate, sha1 prefix, project name) +AddDependencyDialog_searchDone=Done. {0} results found. +AddDependencyDialog_searchError=Error while searching: {0} +AddDependencyDialog_searching=Searching... +AddDependencyDialog_searchingFor=Searching for {0}... +AddDependencyDialog_title=Add Dependency +AddDependencyDialog_tooManyResults=Too many results. Please refine your search. +AddDependencyDialog_transitive_dependency=\ \ + {0}-{1}-{2} is already a transitive dependency.\n +AddDependencyDialog_version_label=Version: +AddPluginAction_searchDialog_title=Add Plugin +ArchetypeCatalogFactory_default_local=Default Local +ArchetypeCatalogFactory_error_missing_catalog=Error looking up archetype catalog; {0} +ArchetypeCatalogFactory_indexer_catalog=Nexus Indexer +ArchetypeCatalogFactory_internal=Internal +ArchetypeCatalogFactory_local=Local {0} +ArchetypeCatalogFactory_remote=Remote {0} +ArchetypeCatalogsWriter_error_parse=Unable to parse Archetype catalogs list; {0} +ArchetypeCatalogsWriter_error_write=Unable to write Archetype catalogs; {0} +AsyncFetcher_error_cancelled=transfer has been cancelled by user +AsyncFetcher_error_server=Server returned status code {0}: {1} +AsyncFetcher_task_fetching=Fetching {0} +AsyncFetcher_task_fetching2=Fetching {0} ({1}%) +ChangeNatureAction_job_changing=Changing nature +ChangeNatureAction_status_error=Can't change nature +CustomArchetypeDialog_error_artid=Archetype Artifact Id is required +CustomArchetypeDialog_error_grid=Archetype Group Id is required +CustomArchetypeDialog_error_version=Archetype Version is required +CustomArchetypeDialog_lblArchetypeartifactid=Archetype Artifact Id: +CustomArchetypeDialog_lblArchetypegroupId=Archetype Group Id: +CustomArchetypeDialog_lblArchetypeversion=Archetype Version: +CustomArchetypeDialog_lblRepo=Repository URL: +CustomArchetypeDialog_message=Specify Archetype and Maven repository URL +CustomizableLifecycleMappingPropertyPage_message=Customizable Lifecycle Mapping +CustomRepositoriesNode_name=Custom Repositories +DefaultMavenMenuCreator_action_ci=Open Continuous Integration +DefaultMavenMenuCreator_action_dependency=Add Dependency +DefaultMavenMenuCreator_action_disable_management=Disable Dependency Management +DefaultMavenMenuCreator_action_disable_workspace=Disable Workspace Resolution +DefaultMavenMenuCreator_action_enable_dm=Enable Dependency Management +DefaultMavenMenuCreator_action_enable_workspace=Enable Workspace Resolution +DefaultMavenMenuCreator_action_issues=Open Issue Tracker +DefaultMavenMenuCreator_action_open_pom=Open POM +DefaultMavenMenuCreator_action_plugin=Add Plugin +DefaultMavenMenuCreator_action_project=Open Project Page +DefaultMavenMenuCreator_action_project_page=Open Project Page +DefaultMavenMenuCreator_action_scm=Open Source Control +DefaultMavenMenuCreator_action_update_config=Update Project Configuration +DefaultMavenMenuCreator_action_update_deps=Update Dependencies +DefaultMavenMenuCreator_action_update_snapshots=Update Snapshots +EclipseLogger_debug1=[DEBUG] {0} +EclipseLogger_debug2=[DEBUG] {0} {1} +EclipseLogger_error1=[ERROR] {0} +EclipseLogger_error2=[ERROR] {0} {1} +EclipseLogger_fatal1=[FATAL ERROR] {0} +EclipseLogger_fatal2=[FATAL ERROR] {0} {1} +EclipseLogger_info1=[INFO] {0} +EclipseLogger_info2=[INFO] {0} {1} +EclipseLogger_name=m2e console logger +EclipseLogger_warn1=[WARN] {0} +EclipseLogger_warn2=[WARN] {0} {1} +EditDependencyDialog_artifactId_label=Artifact Id: +EditDependencyDialog_classifier_label=Classifier: +EditDependencyDialog_groupId_label=Group Id: +EditDependencyDialog_optional_checkbox=Optional +EditDependencyDialog_scope_label=Scope: +EditDependencyDialog_systemPath_label=System Path: +EditDependencyDialog_title=Dependency Properties +EditDependencyDialog_type_label=Type: +EditDependencyDialog_version_label=Version: +EmptyLifecycleMappingPropertyPage_title=No lifecycle mapping info to display for the Empty Lifecycle Mapping +ExtensionReader_foundLifecycleMapping=Found lifecycle mapping id="{0}", name="{1}". +EnableNatureAction_job_enable=Enabling Maven Dependency Management +EnableNatureAction_wizard_shell=Create new POM +GlobalRepositoriesNode_name=Global Repositories +IndexedArtifactNode_no_pack=[No Packaging] +IndexUpdaterJob_title=Updating indexes +LifecycleConfigurationMojoExecutionNotCovered=Mojo execution not covered by lifecycle configuration: {0} (maven lifecycle phase: {1}) +LifecycleMissing=Unknown or missing lifecycle mapping with id="{0}" (project packaging type="{1}") +LocalArchetypeCatalogDialog_btnBrowse=&Browse... +LocalArchetypeCatalogDialog_dialog_title=Select Archetype catalog +LocalArchetypeCatalogDialog_error_empty=Archetype catalog is empty +LocalArchetypeCatalogDialog_error_exist=Archetype catalog does not exist +LocalArchetypeCatalogDialog_error_no_location=Archetype catalog location is required +LocalArchetypeCatalogDialog_lblCatalog=&Catalog File: +LocalArchetypeCatalogDialog_lblDesc=Description: +LocalArchetypeCatalogDialog_message=Specify catalog location and description +LocalArchetypeCatalogDialog_title=Local Archetype Catalog +LocalProjectScanner_task_scanning=Scanning folders +LocalRepositoryNode_local=Local Repository +LocalRepositoryRootNode_name=Local Repositories +M2EErrorDialog_column_error=Error +M2EErrorDialog_column_name=Project Name +MavenArchetypesPreferencePage_btnAddLocal=Add &Local Catalog... +MavenArchetypesPreferencePage_btnAddRemote=Add &Remote Catalog... +MavenArchetypesPreferencePage_btnEdit=&Edit... +MavenArchetypesPreferencePage_btnRemove=&Remove +MavenArchetypesPreferencePage_error=Can't save archetype catalog configuration\n{0} +MavenArchetypesPreferencePage_link=Add, remove or edit <a href="\#">Maven Archetype catalogs</a>: +MavenArchetypesPreferencePage_local=Local: {0} +MavenArchetypesPreferencePage_packaged=Packaged: {0} +MavenArchetypesPreferencePage_remote=Remote: {0} +MavenArchetypesPreferencePage_title=Maven Archetype Catalogs +MavenCheckoutLocationPage_btnBrowse=&Browse... +MavenCheckoutLocationPage_btnCheckout=Check out &All projects +MavenCheckoutLocationPage_btnHead=Check out &Head Revision +MavenCheckoutLocationPage_btnRevSelect=&Select... +MavenCheckoutLocationPage_description=Select target location and revision +MavenCheckoutLocationPage_error_empty=Select SCM type and URL +MavenCheckoutLocationPage_error_empty_url=SCM URL field is required +MavenCheckoutLocationPage_error_scm_empty=SCM revision fied is required +MavenCheckoutLocationPage_error_scm_invalid=SCM revision is invalid +MavenCheckoutLocationPage_error_url_empty=SCM URL is invalid +MavenCheckoutLocationPage_lblRevision=&Revision: +MavenCheckoutLocationPage_lblurl=SCM &URL: +MavenCheckoutLocationPage_title=Target Location +MavenCheckoutOperation_task_checking=Checking out {0} {1} +MavenCheckoutOperation_task_scanning=Scanning {0} {1} +MavenCheckoutWizard_location1=Select Project Location +MavenCheckoutWizard_location2=Select project location and working set +MavenCheckoutWizard_title=Checkout as Maven project from SCM +MavenConsoleImpl_title=Maven Console +MavenConsolePageParticipant_any=Show Console on Any Output +MavenConsolePageParticipant_error=Show Console on Error +MavenConsoleRemoveAction_tooltip=Close Maven2 Console +MavenDebugOutputAction_0=Debug Output +MavenDependenciesWizardPage_lblArtifacts=Maven Artifacts: +MavenDependenciesWizardPage_searchDialog_title=Add Dependency +MavenExternalRuntime_error_cannot_parse=Can't parse m2.conf +MavenExternalRuntime_exc_unsupported=Unsupported m2.conf element +MavenExternalRuntime_unknown=UNKNOWN +MavenEmbeddedRuntime_unknown=UNKNOWN +MavenGoalSelectionDialog_btnQualified=Use &Qualified Name +MavenGoalSelectionDialog_error=Should select at least one goal +MavenGoalSelectionDialog_lblSelect=&Select Goal: +MavenGoalSelectionDialog_message=Select goal: +MavenImpl_error_calc_build_plan=Could not calculate build plan +MavenImpl_error_create_repo=Could not create artifact repository +MavenImpl_error_init_maven=Could not initialize embedded maven runtime +MavenImpl_error_lookup=Could not lookup required component +MavenImpl_error_missing=Missing {0} +MavenImpl_error_mojo=Could not get configured mojo for {0} +MavenImpl_error_no_exec_req=Could not create maven execution request +MavenImpl_error_param=Could not get mojo execution paramater value +MavenImpl_error_read_config=Could not read Maven configuration +MavenImpl_error_read_lastUpdated=Could not read artifact lastUpdated status +MavenImpl_error_read_pom=Could not read pom.xml +MavenImpl_error_read_project=Could not read maven project +MavenImpl_error_read_settings=Could not read settings.xml +MavenImpl_error_read_settings2=Can not read settings file {0} +MavenImpl_error_resolve=Could not resolve artifact +MavenImpl_error_sort=unable to sort projects +MavenImpl_error_write_lastUpdated=Could not write artifact lastUpdated status +MavenImpl_error_write_pom=Could not write pom.xml +MavenImpl_error_write_settings=Could not write settings.xml +MavenImportWizard_job=Importing Maven projects +MavenImportWizard_title=Import Maven projects +MavenImportWizardPage_desc=Select Maven projects +MavenImportWizardPage_inherited=[inherited] +MavenImportWizardPage_title=Maven Projects +MavenInstallationsPreferencePage_btnAdd=&Add... +MavenInstallationsPreferencePage_btnEdit=&Edit... +MavenInstallationsPreferencePage_btnGlobalBrowse=&Browse... +MavenInstallationsPreferencePage_btnRemove=&Remove +MavenInstallationsPreferencePage_dialog_install_message=Select Maven installation directory +MavenInstallationsPreferencePage_dialog_install_title=Maven Installation +MavenInstallationsPreferencePage_dialog_message=Select Maven installation directory +MavenInstallationsPreferencePage_dialog_title=Maven Installation +MavenInstallationsPreferencePage_error_global_missing=Global settings file doesn't exist +MavenInstallationsPreferencePage_error_global_parse=Unable to parse global settings file; +MavenInstallationsPreferencePage_error_message=Select the directory where Maven is installed. +MavenInstallationsPreferencePage_error_title=Maven Install +MavenInstallationsPreferencePage_error2_message=The selected directory is not a valid Maven directory. +MavenInstallationsPreferencePage_error3_message=The selected Maven install is already registered. +MavenInstallationsPreferencePage_error4_message=Selected Maven install is already registered +MavenInstallationsPreferencePage_job_updating=Updating Maven installation settings +MavenInstallationsPreferencePage_lblNote1=Note: Embedded runtime is always used for dependency resolution, but +MavenInstallationsPreferencePage_lblNote2=does not use global settings when it is used to launch Maven. To learn more, visit the <a href="http://maven.apache.org/">maven</a> web page. +MavenInstallationsPreferencePage_link=Select the installation used to launch maven: +MavenInstallationsPreferencePage_link_global=Open editor for global settings +MavenInstallationsPreferencePage_link_open=\ (<a href="\#">open file</a>): +MavenInstallationsPreferencePage_settings=Global settings +MavenInstallationsPreferencePage_settings_install=Global settings from installation directory +MavenInstallationsPreferencePage_title=Maven Installations +MavenInstallFileArtifactWizardPage_btnChecksum=Create C&hecksum +MavenInstallFileArtifactWizardPage_btnFilename=&Browse... +MavenInstallFileArtifactWizardPage_btnGenerate=Gen&erate POM +MavenInstallFileArtifactWizardPage_btnPom=B&rowse... +MavenInstallFileArtifactWizardPage_desc=Install file in local repository +MavenInstallFileArtifactWizardPage_error_artifactid=Artifact Id must be specified +MavenInstallFileArtifactWizardPage_error_groupid=Group Id must be specified +MavenInstallFileArtifactWizardPage_error_missing=Artifact file does not exist +MavenInstallFileArtifactWizardPage_error_missingpom=POM file does not exist +MavenInstallFileArtifactWizardPage_error_no_name=Artifact file name must be specified +MavenInstallFileArtifactWizardPage_error_packaging=Packaging must be specified +MavenInstallFileArtifactWizardPage_error_version=Version must be specified +MavenInstallFileArtifactWizardPage_file_title=Select file +MavenInstallFileArtifactWizardPage_lblArtifact=&Artifact Id: +MavenInstallFileArtifactWizardPage_lblClassifier=&Classifier: +MavenInstallFileArtifactWizardPage_lblFileName=Artifact &file: +MavenInstallFileArtifactWizardPage_lblgroupid=&Group Id: +MavenInstallFileArtifactWizardPage_lblPackaging=&Packaging: +MavenInstallFileArtifactWizardPage_lblPom=&POM file: +MavenInstallFileArtifactWizardPage_lblVersion=&Version: +MavenInstallFileArtifactWizardPage_message=Selected artifact corresponds to {0} +MavenInstallFileArtifactWizardPage_title=Install file in local repository +MavenInstallFileWizard_error=Execution error +MavenInstallFileWizard_job=Installing artifact +MavenInstallFileWizard_title=Install artifact +MavenMarkerManager_duplicate_groupid=GroupId is duplicate of parent groupId +MavenMarkerManager_duplicate_version=Version is duplicate of parent version +MavenMarkerManager_error_artifact=Artifact error +MavenMarkerManager_error_missing=Missing artifact {0} +MavenMarkerManager_error_noschema=There is no schema defined for this pom.xml. +MavenMarkerManager_error_offline=Offline / {0} +MavenMarkerManager_managed_title=Overriding managed version {0} for {1} +MavenMarkerManager_metadata_resolution=Metadata resolution error +MavenMaterializePomWizard_btnCheckout=&Check out All projects +MavenMaterializePomWizard_btnDev=Use &Developer connection +MavenMaterializePomWizard_dialog_message=Select Maven artifacts to import +MavenMaterializePomWizard_dialog_title=Select Maven artifacts +MavenMaterializePomWizard_location_message=Select project location and working set +MavenMaterializePomWizard_location_title=Select project location +MavenMaterializePomWizard_title=Import Maven Projects +MavenModelManager_error_cannot_load=Can't load model {0} +MavenModelManager_error_create=Can't create model {0}; {1} +MavenModelManager_error_pom_exists=POM {0} already exists +MavenModelManager_error_read=Project read error +MavenModelManager_monitor_building=Building dependency tree +MavenModelManager_monitor_reading=Reading project +MavenModuleWizardParentPage_error=The parent project must have a packaging type of POM +MavenPlugin_error_jre_message=The Maven Integration requires that Eclipse be running in a JDK, because a number of Maven core plugins are using jars from the JDK.\n\nPlease make sure the -vm option in <a>eclipse.ini</a> is pointing to a JDK and verify that <a>Installed JREs</a> are also using JDK installs. +MavenPlugin_error_jre_title=Maven Integration for Eclipse JDK Warning +MavenPlugin_error_warn_again=Do not warn again +MavenPomSelectionComponent_btnJavadoc=Include Javadocs +MavenPomSelectionComponent_btnSource=Include Sources +MavenPomSelectionComponent_btnTests=Include Tests +MavenPomSelectionComponent_detail1={0} ({1}) +MavenPomSelectionComponent_details2={0}, size: {1} b +MavenPomSelectionComponent_error=Search error: {0} +MavenPomSelectionComponent_lblResults=&Search Results: +MavenPomSelectionComponent_managed_decoration=\ \ (managed) +MavenPomSelectionComponent_nosel=No selection +MavenPomSelectionComponent_results=Results for ''{0}'' ({1}) +MavenPomSelectionComponent_search_title=&Enter groupId, artifactId or sha1 prefix or pattern (*): +MavenPomSelectionComponent_searching=Searching ''{0}''... +MavenPomSelectionComponent_searchJob=Repository search +MavenPomSelectionComponent_selected=Selected {0} +MavenPomSelectionComponent_toomany=Too many results to display. Enter a more specific search term. +MavenPomSelectionComponent_tooshort=Query ''{0}'' is too short +MavenPomWizard_error_exists=POM already exists +MavenPomWizard_error_title=Error +MavenPomWizard_status_not_exists=Folder "{0}" does not exist. +MavenPomWizard_task=Creating POM +MavenPomWizard_title=Maven POM wizard +MavenPomWizardPage_btnBrowse=Browse... +MavenPomWizardPage_desc=This wizard creates a new POM (pom.xml) descriptor for Maven2. +MavenPomWizardPage_dialog_title=Select project +MavenPomWizardPage_error_artid=Artifact Id must be specified +MavenPomWizardPage_error_folder=Project or folder must be specified +MavenPomWizardPage_error_folder_write=Folder must be writable +MavenPomWizardPage_error_folder2=Folder must exist +MavenPomWizardPage_error_grid=Group Id must be specified +MavenPomWizardPage_error_pack=Packaging must be specified +MavenPomWizardPage_error_version=Version must be specified +MavenPomWizardPage_lblProject=&Project: +MavenPomWizardPage_title=Maven2 POM +MavenPreferencePage_download=Download repository index updates on startup +MavenPreferencePage_hide=Hide folders of physically nested modules (experimental) +MavenPreferencePage_select=&Select... +MavenPreferencePage_select2=S&elect... +MavenPreferencePage_update=Update Maven projects on startup +MavenProjectCheckoutJob_confirm_message=No Maven projects found, but there is Eclipse projects configuration avaialble.\nDo you want to select and import Eclipse projects? +MavenProjectCheckoutJob_confirm_title=Project Import +MavenProjectCheckoutJob_confirm2_message=No Maven projects found. Do you want to create project using new project wizard?\nCheck out location will be copied into clipboard. +MavenProjectCheckoutJob_confirm2_title=Project Import +MavenProjectCheckoutJob_job=Importing Maven projects +MavenProjectCheckoutJob_title=Checking out Maven projects +MavenProjectFacade_error=Could not read maven project +MavenProjectLifecycleMappingPage_error_no_page=No lifecycle mapping property page found. +MavenProjectLifecycleMappingPage_error_no_strategy=No lifecycle mapping strategy found. +MavenProjectLifecycleMappingPage_error_page_error=Unable to load the lifecycle mapping property page. +MavenProjectPomScanner_23=Reading model {0}:{1}:{2} +MavenProjectPomScanner_task_resolving=Resolving artifact {0}:{1}:{2} +MavenProjectPreferencePage_btnResolve=Resolve dependencies from &Workspace projects +MavenProjectPreferencePage_dialog_message=Maven settings has changed. Do you want to update project configuration? +MavenProjectPreferencePage_dialog_title=Maven Settings +MavenProjectPreferencePage_job=Updating {0} Sources +MavenProjectPreferencePage_lblProfiles=Active Maven &Profiles (comma separated): +MavenProjectPreferencePage_title=Maven +MavenProjectWizardArchetypePage_add_title=Add Archetype +MavenProjectWizardArchetypePage_all=All Catalogs +MavenProjectWizardArchetypePage_btnAdd=&Add Archetype... +MavenProjectWizardArchetypePage_btnConfigure=Con&figure... +MavenProjectWizardArchetypePage_btnLast=&Show the last version of Archetype only +MavenProjectWizardArchetypePage_btnSnapshots=&Include snapshot archetypes +MavenProjectWizardArchetypePage_error_no=No archetypes currently available. The archetype list will refresh when the indexes finish updating. +MavenProjectWizardArchetypePage_error_read=Unable to read catalog factory. +MavenProjectWizardArchetypePage_error_resolve=The archetype {0} could not be resolved. +MavenProjectWizardArchetypePage_error_resolve2=Can't resolve Archetype {0} +MavenProjectWizardArchetypePage_lblCatalog=C&atalog: +MavenProjectWizardArchetypePage_lblFilter=&Filter: +MavenProjectWizardArchetypePage_task_downloading=Downloading Archetype +MavenProjectWizardArchetypePage_task_indexing=indexing... +MavenProjectWizardArchetypePage_task_reading=reading project... +MavenProjectWizardArchetypePage_task_resolving=resolving POM... +MavenProjectWizardArchetypePage_task_resolving2=resolving JAR... +MavenProjectWizardArchetypeParametersPage_btnAdd=&Add... +MavenProjectWizardArchetypeParametersPage_btnRemove=&Remove +MavenProjectWizardArchetypeParametersPage_columnName=Name +MavenProjectWizardArchetypeParametersPage_columnValue=Value +MavenProjectWizardArchetypeParametersPage_error_download=Error downloading archetype {0} +MavenProjectWizardArchetypeParametersPage_error_package=Invalid package name +MavenProjectWizardArchetypeParametersPage_lblProps=Properties available from archetype: +MavenProjectWizardArchetypeParametersPage_task=Downloading Archetype {0} +MavenProjectWizardArtifactPage_searchDialog_title=Select Parent Artifact +MavenProjectWizardLocationPage_btnLocation=Brows&e... +MavenProjectWizardLocationPage_btnUserDefault=Use default &Workspace location +MavenProjectWizardLocationPage_dialog_location=Select Location +MavenProjectWizardLocationPage_lblLocation=&Location: +MavenRepositorySearchDialog_7=compile +MavenRepositorySearchDialog_lblScope=Scope: +MavenRepositoryView_action_copy=Copy URL +MavenRepositoryView_action_copy_tooltip=Copy URL to Clipboard +MavenRepositoryView_action_disable_tooltip=Disable repository index +MavenRepositoryView_action_enable_tooltip=Enable minimal repository index +MavenRepositoryView_action_enableFull_tooltip=Enable full repository index +MavenRepositoryView_action_materialize=Materialize Projects +MavenRepositoryView_action_open=Open POM +MavenRepositoryView_action_open_tooltip=Open Maven POM +MavenRepositoryView_action_rebuild=Rebuild Index +MavenRepositoryView_action_rebuild_tooltip=Force a rebuild of the maven index +MavenRepositoryView_action_reload=Reload settings.xml +MavenRepositoryView_action_update=Update Index +MavenRepositoryView_btnCollapse=Collapse All +MavenRepositoryView_btnCollapse_tooltip=Collapse All +MavenRepositoryView_btnUpdate_tooltip=Update repository index +MavenRepositoryView_details_disabled=Index Details Disabled +MavenRepositoryView_disable_details=Disable Index Details +MavenRepositoryView_enable_full=Enable Full Index +MavenRepositoryView_enable_minimum=Enable Minimum Index +MavenRepositoryView_enabled_full=Full Index Enabled +MavenRepositoryView_error_message=Unable to set the index details due to the following error:\n +MavenRepositoryView_error_title=Error Setting Index Details +MavenRepositoryView_job_reloading=Reloading settings.xml +MavenRepositoryView_minimum_enabled=Minimum Index Enabled +MavenRepositoryView_rebuild_many=Rebuild Indexes +MavenRepositoryView_rebuild_msg=Are you sure you want to rebuild the index ''{0}'' +MavenRepositoryView_rebuild_msg2=Are you sure you want to rebuild the selected indexes? +MavenRepositoryView_rebuild_one=Rebuild Index +MavenRepositoryView_rebuild_title=Rebuild Index +MavenRepositoryView_rebuild_title2=Rebuild Indexes +MavenRepositoryView_reload_msg=This will reload the settings.xml and rebuild the indexes for the repositories. Are you sure you want to reload the settings? +MavenRepositoryView_reload_title=Reload settings.xml +MavenRepositoryView_update_more=Update Indexes +MavenRepositoryView_update_one=Update Index +MavenSearchPage_btnBrowse=Browse... +MavenSearchPage_btnSelect=Select All +MavenSearchPage_btnUnselect=Deselect All +MavenSearchPage_lblArtifactid=Artifact Id: +MavenSearchPage_lblClass=Class Name: +MavenSearchPage_lblGroupid=Group Id: +MavenSearchPage_lblPackaging=Packaging: +MavenSearchPage_lblRepos=Repositories: +MavenSearchPage_lblSha=SHA1: +MavenSearchPage_lblVersion=Version: +MavenSearchPage_separator=Label +MavenSettingsPreferencePage_btnBrowse=&Browse... +MavenSettingsPreferencePage_btnUpdate=Update Settings +MavenSettingsPreferencePage_error_missing=User settings file doesn't exist +MavenSettingsPreferencePage_error_parse=Unable to parse user settings file; {0} +MavenSettingsPreferencePage_job_indexing=Indexing Local Repository... +MavenSettingsPreferencePage_job_updating=Updating Maven settings +MavenSettingsPreferencePage_lblLocal=Local Repository (From merged user and global settings): +MavenSettingsPreferencePage_link_tooltip=Open editor for user settings +MavenSettingsPreferencePage_link1=User &Settings: +MavenSettingsPreferencePage_link2=User &Settings (<a href="\#">open file</a>): +MavenSettingsPreferencePage_task_updating=Updating progress for {0} +MavenSettingsPreferencePage_title=Maven User Settings +MissingLifecycleMapping_name=Unknown or missing lifecycle mapping +MissingLifecycleMappingPropertyPage_error=Unknown or missing lifecycle mapping with id=`{0}'. \nCheck the spelling and/or install required Eclipse plugins. +MissingLifecycleMappingPropertyPage_title=Missing lifecycle mapping +NexusIndexManager_78=nexus-indexer +NexusIndexManager_error_add_repo=Could not add repository index +NexusIndexManager_error_read_index=Could not read index details file +NexusIndexManager_error_reindexing=Reindexing error +NexusIndexManager_error_root_grp=Can't get root groups for {0} +NexusIndexManager_error_search=Search error +NexusIndexManager_error_unexpected=Unexpected exception +NexusIndexManager_error_write_index=Could not write index details file +NexusIndexManager_inherited=[inherited] +NexusIndexManager_task_updating=Updating index {0} +OpenPomAction_33=Can't open editor for {0}\n{1} +OpenPomAction_error_download=Can't download {0} +OpenPomAction_error_download_source=Can't download sources for {0} +OpenPomAction_error_open_editor=Can't open editor for {0} +OpenPomAction_error_open_pom=Can't open pom file for {0} +OpenPomAction_job_opening=Opening POM +OpenPomAction_open_error_message=Unable to read Maven project +OpenPomAction_open_error_title=Open POM +OpenPomAction_open_title=Open Maven POM +OpenPomAction_title_pom=Search Maven POM +OpenUrlAction_browser_title=Open Browser +OpenUrlAction_error_no_ci=Project does't specify Continuous Integration URL +OpenUrlAction_error_no_issues=Project does't specify issue management URL +OpenUrlAction_error_no_scm=Project does't specify SCM URL +OpenUrlAction_error_no_url=Project does't specify project URL +OpenUrlAction_error_open=Can't download {0} POM +OpenUrlAction_job_browser=Opening Browser +OpenUrlAction_open_url_message=Unable to read Maven project +OpenUrlAction_open_url_title=Open URL +PomFileContentDescriber_error=Internal Error: XML parser configuration error during content description for Maven POM files +ProjectConfigurationManager_0=Can't get canonical file for {0} +ProjectConfigurationManager_error_failed=Failed to create project. +ProjectConfigurationManager_error_rename=Can't rename {0} +ProjectConfigurationManager_error_resolve=Could not resolve archetype +ProjectConfigurationManager_error_resolve2=\ from any of the configured repositories. +ProjectConfigurationManager_error_targetDir=\ Target directory {0} already exists. +ProjectConfigurationManager_error_unable_archetype=Unable to create project from archetype {0} +ProjectConfigurationManager_task_configuring=Configuring Maven projects +ProjectConfigurationManager_task_creating=Creating project {0} +ProjectConfigurationManager_task_creating_folders=Creating project folders... +ProjectConfigurationManager_task_creating_pom=Creating the POM file... +ProjectConfigurationManager_task_creating_project=Configuring project... +ProjectConfigurationManager_task_creating_project1=Creating project {0} +ProjectConfigurationManager_task_creating_workspace=Creating workspace project... +ProjectConfigurationManager_task_disable_nature=Disable Maven nature +ProjectConfigurationManager_task_enable_nature=Enable Maven nature +ProjectConfigurationManager_task_executing_archetype=Executing Archetype {0}:{1} +ProjectConfigurationManager_task_importing=Importing Maven projects +ProjectConfigurationManager_task_importing2=Importing project {0} +ProjectConfigurationManager_task_refreshing=Refreshing projects +ProjectConfigurationManager_task_updating=Updating configuration for {0} +ProjectConfiguratorsTable_column_id=Id +ProjectConfiguratorsTable_column_name=Name +ProjectConfiguratorsTableContentProvider_no_configs=No Project Configurators +ProjectRegistryManager_task_project=project {0} +ProjectRegistryManager_task_refreshing=Refreshing projects +ProjectRegistryRefreshJob_task_refreshing=Refreshing Maven model +ProjectRegistryRefreshJob_title=Updating Maven Dependencies +ProjectRepositoriesNode_name=Project Repositories +ProjectsImportPage_btnDeselect=&Deselect All +ProjectsImportPage_btnRefresh=R&efresh +ProjectsImportPage_btnSelect=&Select All +ProjectsImportPage_desc=Select non-Maven projects to import +ProjectsImportPage_dialog_title=Question +ProjectsImportPage_error_creation=Creation Problems +ProjectsImportPage_lstProjects=&Projects: +ProjectsImportPage_message=Some projects were hidden because they exist in the workspace directory +ProjectsImportPage_overwrite=''{0}'' already exists. Would you like to overwrite it? +ProjectsImportPage_overwrite2=Overwrite ''{0}'' in folder ''{1}''? +ProjectsImportPage_task_checking=Checking: {0} +ProjectsImportPage_task_creating=Creating Projects +ProjectsImportPage_task_processing=Processing results +ProjectsImportPage_task_search=Searching for projects +ProjectsImportPage_title=Import Projects +ProjectsImportWizard_title=Import +RemoteArchetypeCatalogDialog_btnVerify=&Verify... +RemoteArchetypeCatalogDialog_error_empty=Remote catalog is empty +RemoteArchetypeCatalogDialog_error_read=Unable to read remote catalog;\n{0} +RemoteArchetypeCatalogDialog_error_required=Archetype catalog url is required +RemoteArchetypeCatalogDialog_job_download=Downloading remote catalog +RemoteArchetypeCatalogDialog_lblCatalog=&Catalog File: +RemoteArchetypeCatalogDialog_lblDesc=Description: +RemoteArchetypeCatalogDialog_message=Specify catalog url and description +RemoteArchetypeCatalogDialog_message_found=Found {0} archetype(s) +RemoteArchetypeCatalogDialog_title=Remote Archetype Catalog +RepositoryNode_updating=\ [updating] +RepositoryRegistryUpdateJob_title=Repository registry initialization +ScmUrl_error=Invalid SCM url {0} +SelectionUtil_error_cannot_read=Can't read Maven project +UpdateSourcesAction_error_cannot_update=Unable to update Maven configuration +UpdateSourcesAction_error_message=Unable to update maven configuration for the following projects: +UpdateSourcesAction_error_title=Error Updating Maven Configuration +UpdateSourcesAction_job_update_conf=Updating Maven Configuration +WorkingSetGroup_btnAddSet=&Add project(s) to working set +WorkingSetGroup_btnMore=Mor&e... +WorkingSetGroup_lblSet=Wo&rking set: +WorkspaceRepositoryNode_name=Workspace Projects diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceConstants.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceConstants.java new file mode 100644 index 00000000..46dfeae0 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceConstants.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.preferences; + + +/** + * Maven preferences constants + */ +public interface MavenPreferenceConstants { + + static final String PREFIX = "eclipse.m2."; //$NON-NLS-1$ + + /** String */ + // public static final String P_LOCAL_REPOSITORY_DIR = PREFIX+"localRepositoryDirectory"; + + /** true or false */ + // public static final String P_CHECK_LATEST_PLUGIN_VERSION = PREFIX+"checkLatestPluginVersion"; + + /** String ??? */ + // public static final String P_GLOBAL_CHECKSUM_POLICY = PREFIX+"globalChecksumPolicy"; + + /** boolean */ + public static final String P_OFFLINE = PREFIX + "offline"; //$NON-NLS-1$ + + /** boolean */ + // public static final String P_UPDATE_SNAPSHOTS = PREFIX+"updateSnapshots"; + + /** boolean */ + public static final String P_DEBUG_OUTPUT = PREFIX + "debugOutput"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_DOWNLOAD_SOURCES = PREFIX + "downloadSources"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_DOWNLOAD_JAVADOC = PREFIX + "downloadJavadoc"; //$NON-NLS-1$ + + /** String */ + public static final String P_GLOBAL_SETTINGS_FILE = PREFIX + "globalSettingsFile"; //$NON-NLS-1$ + + /** String */ + public static final String P_USER_SETTINGS_FILE = PREFIX + "userSettingsFile"; //$NON-NLS-1$ + + /** String */ + public static final String P_OUTPUT_FOLDER = PREFIX + "outputFolder"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_DISABLE_JDK_WARNING = PREFIX + "disableJdkwarning"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_DISABLE_JDK_CHECK = PREFIX + "disableJdkCheck"; //$NON-NLS-1$ + + /** String */ + public static final String P_RUNTIMES = PREFIX + "runtimes"; //$NON-NLS-1$ + + /** String */ + public static final String P_DEFAULT_RUNTIME = PREFIX + "defaultRuntime"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_UPDATE_INDEXES = PREFIX + "updateIndexes"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_UPDATE_PROJECTS = PREFIX + "updateProjects"; //$NON-NLS-1$ + + /** String */ + public static final String P_JIRA_USERNAME = PREFIX + "jiraUsername"; //$NON-NLS-1$ + + /** String */ + public static final String P_JIRA_PASSWORD = PREFIX + "jiraPassword"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_HIDE_FOLDERS_OF_NESTED_PROJECTS = PREFIX + "hideFoldersOfNestedProjects"; //$NON-NLS-1$ + + public static final String P_SHOW_CONSOLE_ON_ERR = PREFIX+"showConsoleOnErr"; //$NON-NLS-1$ + + public static final String P_SHOW_CONSOLE_ON_OUTPUT = PREFIX+"showConsoleOnOutput"; //$NON-NLS-1$ + + /** boolean */ + public static final String P_FULL_INDEX= PREFIX+"fullIndex"; //$NON-NLS-1$ +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceInitializer.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceInitializer.java new file mode 100644 index 00000000..b9ade3d4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/preferences/MavenPreferenceInitializer.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; + +import org.apache.maven.cli.MavenCli; + +import org.eclipse.m2e.core.MavenPlugin; + + +/** + * Maven preferences initializer. + * + * @author Eugene Kuleshov + */ +public class MavenPreferenceInitializer extends AbstractPreferenceInitializer { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = MavenPlugin.getDefault().getPreferenceStore(); + + store.setDefault(MavenPreferenceConstants.P_USER_SETTINGS_FILE, // + MavenCli.DEFAULT_USER_SETTINGS_FILE.getAbsolutePath()); + + store.setDefault(MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE, ""); //$NON-NLS-1$ + + store.setDefault(MavenPreferenceConstants.P_DEBUG_OUTPUT, false); + + store.setDefault(MavenPreferenceConstants.P_OFFLINE, false); + + store.setDefault(MavenPreferenceConstants.P_DOWNLOAD_SOURCES, false); + store.setDefault(MavenPreferenceConstants.P_DOWNLOAD_JAVADOC, false); + + // store.setDefault( MavenPreferenceConstants.P_GLOBAL_CHECKSUM_POLICY, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); + // store.setDefault( MavenPreferenceConstants.P_UPDATE_SNAPSHOTS, false); + // store.setDefault( MavenPreferenceConstants.P_CHECK_LATEST_PLUGIN_VERSION, false); + + store.setDefault(MavenPreferenceConstants.P_OUTPUT_FOLDER, "target-eclipse"); //$NON-NLS-1$ + + store.setDefault(MavenPreferenceConstants.P_RUNTIMES, ""); //$NON-NLS-1$ + store.setDefault(MavenPreferenceConstants.P_DEFAULT_RUNTIME, ""); //$NON-NLS-1$ + + store.setDefault(MavenPreferenceConstants.P_UPDATE_INDEXES, true); + store.setDefault(MavenPreferenceConstants.P_UPDATE_PROJECTS, false); + + store.setDefault(MavenPreferenceConstants.P_HIDE_FOLDERS_OF_NESTED_PROJECTS, false); + + store.setDefault(MavenPreferenceConstants.P_SHOW_CONSOLE_ON_ERR, true); + store.setDefault(MavenPreferenceConstants.P_SHOW_CONSOLE_ON_OUTPUT, false); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ArtifactKeyAdapterFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ArtifactKeyAdapterFactory.java new file mode 100644 index 00000000..aff02f61 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ArtifactKeyAdapterFactory.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.core.runtime.NullProgressMonitor; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; + + +/** + * Adapter factory for ArtifactKey + * + * @author Igor Fedorenko + */ +@SuppressWarnings("unchecked") +public class ArtifactKeyAdapterFactory implements IAdapterFactory { + + private static final Class[] ADAPTER_LIST = new Class[] {ArtifactKey.class,}; + + public Object getAdapter(Object adaptable, Class adapterType) { + if(!ArtifactKey.class.equals(adapterType)) { + return null; + } + + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + if(adaptable instanceof IProject) { + IProject project = (IProject) adaptable; + IMavenProjectFacade facade = projectManager.create(project, new NullProgressMonitor()); + if(facade != null) { + return facade.getArtifactKey(); + } + } else if(adaptable instanceof IFile) { + IFile file = (IFile) adaptable; + if(IMavenConstants.POM_FILE_NAME.equals(file.getName())) { + IMavenProjectFacade facade = projectManager.create(file, true, new NullProgressMonitor()); + if(facade != null) { + return facade.getArtifactKey(); + } + } + } + + return null; + } + + public Class[] getAdapterList() { + // target type + return ADAPTER_LIST; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/DependencyResolutionContext.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/DependencyResolutionContext.java new file mode 100644 index 00000000..71031ae3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/DependencyResolutionContext.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; + +import org.apache.maven.execution.MavenExecutionRequest; + +import org.eclipse.m2e.core.project.MavenUpdateRequest; + +/** + * @author igor + */ +public class DependencyResolutionContext { + + /** Original update request */ + private final MavenUpdateRequest request; + + /** Set of all pom files to resolve */ + private final Set<IFile> pomFiles = new LinkedHashSet<IFile>(); + + /** Set of pom files to resolve regardless of their isStale() state */ + private final Set<IFile> forcedPomFiles = new HashSet<IFile>(); + + /** The template request for invocations of Maven */ + private MavenExecutionRequest executionRequest; + + public DependencyResolutionContext(MavenUpdateRequest request, MavenExecutionRequest executionRequest) { + this.request = request; + this.pomFiles.addAll(request.getPomFiles()); + this.executionRequest = executionRequest; + } + + public boolean isEmpty() { + return pomFiles.isEmpty(); + } + + public void forcePomFiles(Set<IFile> pomFiles) { + this.pomFiles.addAll(pomFiles); + this.forcedPomFiles.addAll(pomFiles); + } + + public MavenUpdateRequest getRequest() { + return request; + } + + public MavenExecutionRequest getExecutionRequest() { + return executionRequest; + } + + public boolean isForce(IFile pom) { + return request.isForce() || forcedPomFiles.contains(pom); + } + + public IFile pop() { + Iterator<IFile> i = pomFiles.iterator(); + IFile pom = i.next(); + i.remove(); + return pom; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/EclipseMavenMetadataCache.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/EclipseMavenMetadataCache.java new file mode 100644 index 00000000..292a9137 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/EclipseMavenMetadataCache.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +import org.eclipse.core.resources.IFile; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.metadata.ResolutionGroup; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.project.artifact.DefaultMavenMetadataCache; +import org.apache.maven.project.artifact.MavenMetadataCache; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * EclipseMavenMetadataCache + * + * @author igor + */ +public class EclipseMavenMetadataCache extends DefaultMavenMetadataCache implements MavenMetadataCache, IManagedCache { + + public void put(Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository, + List<ArtifactRepository> remoteRepositories, ResolutionGroup result) { + + ArtifactKey gav = new ArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), null); + + if ("pom".equals(artifact.getType()) ) { //$NON-NLS-1$ + // new project pom, remove any existing project entries + removeProject(gav); + } + + super.put(artifact, resolveManagedVersions, localRepository, remoteRepositories, result); + } + + public void removeProject(IFile pom, ArtifactKey key) { + removeProject(key); + } + + private void removeProject(ArtifactKey key) { + if(key == null) { + return; + } + + Iterator<Entry<CacheKey, CacheRecord>> iter = cache.entrySet().iterator(); + + while(iter.hasNext()) { + Entry<CacheKey, CacheRecord> entry = iter.next(); + CacheRecord record = entry.getValue(); + + if(equals(record.getArtifact(), key) || contains(record.getArtifacts(), key)) { + iter.remove(); + } + } + } + + private boolean contains(List<Artifact> artifacts, ArtifactKey key) { + for(Artifact artifact : artifacts) { + if(equals(artifact, key)) { + return true; + } + } + return false; + } + + private boolean equals(Artifact artifact, ArtifactKey key) { + /* + * maybe too conservative, but purge anything that matches GAbV (bV==baseVersion) + */ + return eq(key.getGroupId(), artifact.getGroupId()) // + && eq(key.getArtifactId(), artifact.getArtifactId()) // + && eq(key.getVersion(), artifact.getBaseVersion()); + } + + private static <T> boolean eq(T a, T b) { + return a != null ? a.equals(b) : b == null; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IManagedCache.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IManagedCache.java new file mode 100644 index 00000000..6eb2d41c --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IManagedCache.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + +/** + * IManagedCache + * + * @author igor + */ +public interface IManagedCache { + + /** + * @param pom + * @param mavenProject + */ + void removeProject(IFile pom, ArtifactKey mavenProject); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IgnoreMojoProjectConfiguration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IgnoreMojoProjectConfiguration.java new file mode 100644 index 00000000..b8090934 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/IgnoreMojoProjectConfiguration.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; + + +/** + * IgnoreMojoProjectConfiguration + * + * @author igor + */ +public class IgnoreMojoProjectConfiguration extends AbstractProjectConfigurator { + + public IgnoreMojoProjectConfiguration() { + } + + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) { + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenMarkerManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenMarkerManager.java new file mode 100644 index 00000000..cc1c98a7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenMarkerManager.java @@ -0,0 +1,713 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.w3c.dom.Comment; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.osgi.util.NLS; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.xml.core.internal.parser.regions.TagNameRegion; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; +import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.model.building.ModelBuildingException; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.building.ModelProblem.Severity; +import org.apache.maven.project.DependencyResolutionResult; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuildingException; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +@SuppressWarnings("restriction") +public class MavenMarkerManager implements IMavenMarkerManager { + private static final String XSI_SCHEMA_LOCATION = "xsi:schemaLocation"; //$NON-NLS-1$ + + private static final String PROJECT_NODE = "project"; //$NON-NLS-1$ + public static final String OFFSET = "offset"; //$NON-NLS-1$ + + private final MavenConsole console; + private final IMavenConfiguration mavenConfiguration; + + public MavenMarkerManager(MavenConsole console, IMavenConfiguration mavenConfiguration) { + this.console = console; + this.mavenConfiguration = mavenConfiguration; + } + + public void addMarkers(IResource pomFile, String type, MavenExecutionResult result) { + List<Throwable> exceptions = result.getExceptions(); + + for(Throwable ex : exceptions) { + if(ex instanceof ProjectBuildingException) { + handleProjectBuildingException(pomFile, type, (ProjectBuildingException) ex); + } else if(ex instanceof AbstractArtifactResolutionException) { + AbstractArtifactResolutionException rex = (AbstractArtifactResolutionException) ex; + String errorMessage = getArtifactId(rex) + " " + getErrorMessage(ex); //$NON-NLS-1$ + addMarker(pomFile, type, errorMessage, 1, IMarker.SEVERITY_ERROR); + } else { + handleBuildException(pomFile, type, ex); + } + } + + DependencyResolutionResult resolutionResult = result.getDependencyResolutionResult(); + if(resolutionResult != null) { + // @see also addMissingArtifactMarkers + addErrorMarkers(pomFile, type, org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_metadata_resolution, + resolutionResult.getCollectionErrors()); + for(org.sonatype.aether.graph.Dependency dependency : resolutionResult.getUnresolvedDependencies()) { + addErrorMarkers(pomFile, type, org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_error_artifact, + resolutionResult.getResolutionErrors(dependency)); + } + } + + MavenProject mavenProject = result.getProject(); + if (mavenProject != null) { + addMissingArtifactMarkers(pomFile, type, mavenProject); + } + } + + public void addEditorHintMarkers(IResource pomFile, String type) { + checkForSchema(pomFile, type); + //mkleint: adding here but I'm sort of not entirely clear what the usage patter of this class is. + checkVarious(pomFile, type); + } + + /** + * @param pomFile + */ + private void checkVarious(IResource pomFile, String type) { + IDOMModel domModel = null; + try { + if(!(pomFile instanceof IFile)) { + return; + } + domModel = (IDOMModel) StructuredModelManager.getModelManager().getModelForRead((IFile) pomFile); + IStructuredDocument document = domModel.getStructuredDocument(); + Element root = domModel.getDocument().getDocumentElement(); + + if(root.getNodeName().equals("project")) { //$NON-NLS-1$ + //now check parent version and groupid against the current project's ones.. + checkParentMatchingGroupIdVersion(root, pomFile, type, document); + checkManagedDependencies(root, pomFile, type, document); + checkManagedPlugins(root, pomFile, type, document); + } + } catch(Exception t) { + MavenLogger.log("Error checking for warnings", t); //$NON-NLS-1$ + } finally { + if(domModel != null) { + domModel.releaseFromRead(); + } + } + } + + private void checkManagedDependencies(Element root, IResource pomFile, String type, IStructuredDocument document) + throws CoreException { + IProject prj = pomFile.getProject(); + //the project returned is in a way unrelated to nested child poms that don't have an opened project, + //in that case we pass along a wrong parent/aggregator + if (prj == null || pomFile.getProjectRelativePath().segmentCount() != 1) { + //if the project were the pom's project, the relative path would be just "pom.xml", if it's not just throw it out of the window.. + return; + } + IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager().getProject(prj); + if (facade == null) { + return; + } + MavenProject mavenproject = facade.getMavenProject(); + if (mavenproject == null) { + //we only work with cached instances here, never loading ourselves.. + return; + } + List<Element> candidates = new ArrayList<Element>(); + + Element dependencies = findChildElement(root, "dependencies"); //$NON-NLS-1$ + if (dependencies != null) { + for (Element el : findChildElements(dependencies, "dependency")) { //$NON-NLS-1$ + Element version = findChildElement(el, "version"); //$NON-NLS-1$ + if (version != null) { + candidates.add(el); + } + } + } + //we should also consider <dependencies> section in the profiles, but profile are optional and so is their + // dependencyManagement section.. that makes handling our markers more complex. + // see MavenProject.getInjectedProfileIds() for a list of currently active profiles in effective pom + String currentProjectKey = mavenproject.getGroupId() + ":" + mavenproject.getArtifactId() + ":" + mavenproject.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + List<String> activeprofiles = mavenproject.getInjectedProfileIds().get(currentProjectKey); + //remember what profile we found the dependency in. + Map<Element, String> candidateProfile = new HashMap<Element, String>(); + Element profiles = findChildElement(root, "profiles"); //$NON-NLS-1$ + if (profiles != null) { + for (Element profile : findChildElements(profiles, "profile")) { //$NON-NLS-1$ + String idString = getElementTextValue(findChildElement(profile, "id")); //$NON-NLS-1$ + if (idString != null && activeprofiles.contains(idString)) { + dependencies = findChildElement(profile, "dependencies"); //$NON-NLS-1$ + if (dependencies != null) { + for (Element el : findChildElements(dependencies, "dependency")) { //$NON-NLS-1$ + Element version = findChildElement(el, "version"); //$NON-NLS-1$ + if (version != null) { + candidates.add(el); + candidateProfile.put(el, idString); + } + } + } + } + } + } + //collect the managed dep ids + Map<String, String> managed = new HashMap<String, String>(); + DependencyManagement dm = mavenproject.getDependencyManagement(); + if (dm != null) { + List<Dependency> deps = dm.getDependencies(); + if (deps != null) { + for (Dependency dep : deps) { + //shall we be using geManagementkey() here? but it contains also the type, not only the gr+art ids.. + managed.put(dep.getGroupId() + ":" + dep.getArtifactId(), dep.getVersion()); //$NON-NLS-1$ + } + } + } + + //now we have all the candidates, match them against the effective managed set + for(Element dep : candidates) { + Element version = findChildElement(dep, "version"); //$NON-NLS-1$ + String grpString = getElementTextValue(findChildElement(dep, "groupId")); //$NON-NLS-1$ + String artString = getElementTextValue(findChildElement(dep, "artifactId")); //$NON-NLS-1$ + String versionString = getElementTextValue(version); + if(grpString != null && artString != null && versionString != null) { + String id = grpString + ":" + artString; //$NON-NLS-1$ + if(managed.containsKey(id)) { + String managedVersion = managed.get(id); + if(version instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) version; + if(lookForIgnoreMarker(document, version, off, IMavenConstants.MARKER_IGNORE_MANAGED)) { + continue; + } + + IMarker mark = addMarker(pomFile, type, NLS.bind( + org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_managed_title, managedVersion, artString), + document.getLineOfOffset(off.getStartOffset()) + 1, IMarker.SEVERITY_WARNING, false /*isTransient*/); + mark.setAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, + IMavenConstants.EDITOR_HINT_MANAGED_DEPENDENCY_OVERRIDE); + mark.setAttribute(IMarker.CHAR_START, off.getStartOffset()); + mark.setAttribute(IMarker.CHAR_END, off.getEndOffset()); + mark.setAttribute("problemType", "pomhint"); //only imporant in case we enable the generic xml quick fixes //$NON-NLS-1$ //$NON-NLS-2$ + //add these attributes to easily and deterministicaly find the declaration in question + mark.setAttribute("groupId", grpString); //$NON-NLS-1$ + mark.setAttribute("artifactId", artString); //$NON-NLS-1$ + String profile = candidateProfile.get(dep); + if(profile != null) { + mark.setAttribute("profile", profile); //$NON-NLS-1$ + } + } + } + } + } + } + + static boolean lookForIgnoreMarker(IStructuredDocument document, Element version, IndexedRegion off, String ignoreString) { + Node reg = version; + int line = document.getLineOfOffset(off.getStartOffset()); + try { + int lineend = document.getLineOffset(line) + document.getLineLength(line) - 1; + int start = off.getStartOffset(); + while (reg != null && start < lineend) { + reg = reg.getNextSibling(); + if (reg != null && reg instanceof Comment) { + Comment comm = (Comment)reg; + String data =comm.getData(); + if (data != null && data.contains(ignoreString)) { + return true; + } + } + if (reg != null) { + start = ((IndexedRegion)reg).getStartOffset(); + } + } + } catch(BadLocationException ex) { + //not possible IMHO we ask for line offset of line we know is in the document. + } + return false; + } + + private void checkManagedPlugins(Element root, IResource pomFile, String type, IStructuredDocument document) + throws CoreException { + IProject prj = pomFile.getProject(); + //the project returned is in a way unrelated to nested child poms that don't have an opened project, + //in that case we pass along a wrong parent/aggregator + if (prj == null || pomFile.getProjectRelativePath().segmentCount() != 1) { + //if the project were the pom's project, the relative path would be just "pom.xml", if it's not just throw it out of the window.. + return; + } + IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager().getProject(prj); + if (facade == null) { + return; + } + MavenProject mavenproject = facade.getMavenProject(); + if (mavenproject == null) { + //we only work with cached instances here, never loading ourselves.. + return; + } + List<Element> candidates = new ArrayList<Element>(); + Element build = findChildElement(root, "build"); //$NON-NLS-1$ + if (build == null) { + return; + } + Element plugins = findChildElement(build, "plugins"); //$NON-NLS-1$ + if (plugins != null) { + for (Element el : findChildElements(plugins, "plugin")) { //$NON-NLS-1$ + Element version = findChildElement(el, "version"); //$NON-NLS-1$ + if (version != null) { + candidates.add(el); + } + } + } + //we should also consider <plugins> section in the profiles, but profile are optional and so is their + // pluginManagement section.. that makes handling our markers more complex. + // see MavenProject.getInjectedProfileIds() for a list of currently active profiles in effective pom + String currentProjectKey = mavenproject.getGroupId() + ":" + mavenproject.getArtifactId() + ":" + mavenproject.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + List<String> activeprofiles = mavenproject.getInjectedProfileIds().get(currentProjectKey); + //remember what profile we found the dependency in. + Map<Element, String> candidateProfile = new HashMap<Element, String>(); + Element profiles = findChildElement(root, "profiles"); //$NON-NLS-1$ + if (profiles != null) { + for (Element profile : findChildElements(profiles, "profile")) { //$NON-NLS-1$ + String idString = getElementTextValue(findChildElement(profile, "id")); //$NON-NLS-1$ + if (idString != null && activeprofiles.contains(idString)) { + build = findChildElement(profile, "build"); //$NON-NLS-1$ + if (build == null) { + continue; + } + plugins = findChildElement(build, "plugins"); //$NON-NLS-1$ + if (plugins != null) { + for (Element el : findChildElements(plugins, "plugin")) { //$NON-NLS-1$ + Element version = findChildElement(el, "version"); //$NON-NLS-1$ + if (version != null) { + candidates.add(el); + candidateProfile.put(el, idString); + } + } + } + } + } + } + //collect the managed plugin ids + Map<String, String> managed = new HashMap<String, String>(); + PluginManagement pm = mavenproject.getPluginManagement(); + if (pm != null) { + List<Plugin> plgs = pm.getPlugins(); + if (plgs != null) { + for (Plugin plg : plgs) { + managed.put(plg.getKey(), plg.getVersion()); + } + } + } + + //now we have all the candidates, match them against the effective managed set + for(Element dep : candidates) { + String grpString = getElementTextValue(findChildElement(dep, "groupId")); //$NON-NLS-1$ + if (grpString == null) { + grpString = "org.apache.maven.plugins"; //$NON-NLS-1$ + } + String artString = getElementTextValue(findChildElement(dep, "artifactId")); //$NON-NLS-1$ + Element version = findChildElement(dep, "version"); //$NON-NLS-1$ + String versionString = getElementTextValue(version); + if(grpString != null && artString != null && versionString != null) { + String id = Plugin.constructKey(grpString, artString); + if(managed.containsKey(id)) { + String managedVersion = managed.get(id); + if(version instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) version; + if(lookForIgnoreMarker(document, version, off, IMavenConstants.MARKER_IGNORE_MANAGED)) { + continue; + } + + IMarker mark = addMarker(pomFile, type, NLS.bind( + org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_managed_title, managedVersion, artString), + document.getLineOfOffset(off.getStartOffset()) + 1, IMarker.SEVERITY_WARNING, false /*isTransient*/); + mark.setAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, + IMavenConstants.EDITOR_HINT_MANAGED_PLUGIN_OVERRIDE); + mark.setAttribute(IMarker.CHAR_START, off.getStartOffset()); + mark.setAttribute(IMarker.CHAR_END, off.getEndOffset()); + mark.setAttribute("problemType", "pomhint"); //only imporant in case we enable the generic xml quick fixes //$NON-NLS-1$ //$NON-NLS-2$ + //add these attributes to easily and deterministicaly find the declaration in question + mark.setAttribute("groupId", grpString); //$NON-NLS-1$ + mark.setAttribute("artifactId", artString); //$NON-NLS-1$ + String profile = candidateProfile.get(dep); + if(profile != null) { + mark.setAttribute("profile", profile); //$NON-NLS-1$ + } + } + } + } + } + } + + private void checkParentMatchingGroupIdVersion(Element root, IResource pomFile, String type, + IStructuredDocument document) throws CoreException { + Element parent = findChildElement(root, "parent"); //$NON-NLS-1$ + Element groupId = findChildElement(root, "groupId"); //$NON-NLS-1$ + if(parent != null && groupId != null) { + //now compare the values of parent and project groupid.. + String parentString = getElementTextValue(findChildElement(parent, "groupId")); //$NON-NLS-1$ + String childString = getElementTextValue(groupId); + if(parentString != null && parentString.equals(childString)) { + //now figure out the offset + if(groupId instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) groupId; + IMarker mark = addMarker(pomFile, type, + org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_duplicate_groupid, + document.getLineOfOffset(off.getStartOffset()) + 1, IMarker.SEVERITY_WARNING, false /*isTransient*/); + mark.setAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, IMavenConstants.EDITOR_HINT_PARENT_GROUP_ID); + mark.setAttribute(IMarker.CHAR_START, off.getStartOffset()); + mark.setAttribute(IMarker.CHAR_END, off.getEndOffset()); + mark.setAttribute("problemType", "pomhint"); //only important in case we enable the generic xml quick fixes //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + Element version = findChildElement(root, "version"); //$NON-NLS-1$ + if(parent != null && version != null) { + //now compare the values of parent and project version.. + String parentString = getElementTextValue(findChildElement(parent, "version")); //$NON-NLS-1$ + String childString = getElementTextValue(version); + if(parentString != null && parentString.equals(childString)) { + //now figure out the offset + if(version instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) version; + IMarker mark = addMarker(pomFile, type, + org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_duplicate_version, + document.getLineOfOffset(off.getStartOffset()) + 1, IMarker.SEVERITY_WARNING, false); + mark.setAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, IMavenConstants.EDITOR_HINT_PARENT_VERSION); + mark.setAttribute(IMarker.CHAR_START, off.getStartOffset()); + mark.setAttribute(IMarker.CHAR_END, off.getEndOffset()); + mark.setAttribute("problemType", "pomhint"); //only important in case we enable the generic xml quick fixes //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + } + + public static Element findChildElement(Element parent, String name) { + NodeList rootList = parent.getChildNodes(); + for (int i = 0; i < rootList.getLength(); i++) { + Node nd = rootList.item(i); + if (nd instanceof Element) { + Element el = (Element)nd; + if (name.equals(el.getNodeName())) { + return el; + } + } + } + return null; + } + public static List<Element> findChildElements(Element parent, String name) { + NodeList rootList = parent.getChildNodes(); + List<Element> toRet = new ArrayList<Element>(); + for (int i = 0; i < rootList.getLength(); i++) { + Node nd = rootList.item(i); + if (nd instanceof Element) { + Element el = (Element)nd; + if (name.equals(el.getNodeName())) { + toRet.add(el); + } + } + } + return toRet; + } + + /** + * gets the element text value, accepts null as parameter + * @param element + * @return + */ + public static String getElementTextValue(Node element) { + if (element == null) return null; + StringBuffer buff = new StringBuffer(); + NodeList list = element.getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node child = list.item(i); + if (child instanceof Text) { + Text text = (Text)child; + buff.append(text.getData()); + } + } + return buff.toString(); + } + + /** + * The xsi:schema info is not part of the model, it is stored in the xml only. Need to open the DOM + * and look for the project node to see if it has this schema defined + * @param pomFile + */ + protected void checkForSchema(IResource pomFile, String type) { + IDOMModel domModel = null; + try{ + if(!(pomFile instanceof IFile)){ + return; + } + domModel = (IDOMModel)StructuredModelManager.getModelManager().getModelForRead((IFile)pomFile); + IStructuredDocument document = domModel.getStructuredDocument(); + + // iterate through document regions + documentLoop:for(IStructuredDocumentRegion documentRegion : document.getStructuredDocumentRegions()) { + // only check tag regions + if (DOMRegionContext.XML_TAG_NAME.equals(documentRegion.getType())){ + for(ITextRegion textRegion: documentRegion.getRegions().toArray()){ + // find a project tag + if(textRegion instanceof TagNameRegion && PROJECT_NODE.equals(documentRegion.getText(textRegion))){ + // check if schema is missing + if (documentRegion.getText().lastIndexOf(XSI_SCHEMA_LOCATION) == -1) { + int offset = documentRegion.getStartOffset(); + int lineNumber = document.getLineOfOffset(offset) + 1; + IMarker marker = addMarker(pomFile, type, + org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_error_noschema, lineNumber, + IMarker.SEVERITY_WARNING, false /*isTransient*/); + //the quick fix in the marker view needs to know the offset, since it doesn't have access to the + //editor/source viewer + if(marker != null){ + marker.setAttribute(OFFSET, offset); + marker.setAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, + IMavenConstants.EDITOR_HINT_MISSING_SCHEMA); + marker.setAttribute(IMarker.CHAR_START, documentRegion.getStartOffset()); + marker.setAttribute(IMarker.CHAR_END, documentRegion.getEndOffset()); + marker.setAttribute("problemType", "pomhint"); //only imporant in case we enable the generic xml quick fixes //$NON-NLS-1$ //$NON-NLS-2$ + } + } + // there could only be one project tag + break documentLoop; + } + } + } + } + } catch(Exception ex) { + MavenLogger.log("Error checking for schema", ex); //$NON-NLS-1$ + } + finally { + if ( domModel != null ) { + domModel.releaseFromRead(); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.core.project.IMavenMarkerManager#addMarker(org.eclipse.core.resources.IResource, java.lang.String, int, int) + */ + //just here to satisfy the IMavenMarkerManager contract. + public IMarker addMarker(IResource resource, String type, String message, int lineNumber, int severity) { + return addMarker(resource, type, message, lineNumber, severity, false /*isTransient*/); + } + + private IMarker addMarker(IResource resource, String type, String message, int lineNumber, int severity, boolean isTransient) { + IMarker marker = null; + try { + if(resource.isAccessible()) { + marker = findMarker(resource, type, message, lineNumber, severity, isTransient); + if(marker != null) { + // This marker already exists + return marker; + } + marker= resource.createMarker(type); + marker.setAttribute(IMarker.MESSAGE, message); + marker.setAttribute(IMarker.SEVERITY, severity); + marker.setAttribute(IMarker.TRANSIENT, isTransient); + + if(lineNumber == -1) { + lineNumber = 1; + } + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + } + } catch(CoreException ex) { + console.logError("Unable to add marker; " + ex.toString()); //$NON-NLS-1$ + } + return marker; + } + + private static <T> boolean eq(T a, T b) { + if(a == null) { + if(b == null) { + return true; + } + return false; + } + return a.equals(b); + } + + private IMarker findMarker(IResource resource, String type, String message, int lineNumber, int severity, + boolean isTransient) throws CoreException { + IMarker[] markers = resource.findMarkers(type, false /*includeSubtypes*/, IResource.DEPTH_ZERO); + if(markers == null || markers.length == 0) { + return null; + } + for(IMarker marker : markers) { + if(eq(message, marker.getAttribute(IMarker.MESSAGE)) && eq(lineNumber, marker.getAttribute(IMarker.LINE_NUMBER)) + && eq(severity, marker.getAttribute(IMarker.SEVERITY)) + && eq(isTransient, marker.getAttribute(IMarker.TRANSIENT))) { + return marker; + } + } + return null; + } + + private void handleProjectBuildingException(IResource pomFile, String type, ProjectBuildingException ex) { + Throwable cause = ex.getCause(); + if(cause instanceof ModelBuildingException) { + ModelBuildingException mbe = (ModelBuildingException) cause; + for (ModelProblem problem : mbe.getProblems()) { + String msg = Messages.getString("plugin.markerBuildError", problem.getMessage()); //$NON-NLS-1$ +// console.logError(msg); + int severity = (Severity.WARNING == problem.getSeverity())? IMarker.SEVERITY_WARNING: IMarker.SEVERITY_ERROR; + addMarker(pomFile, type, msg, 1, severity); + } + } else { + handleBuildException(pomFile, type, ex); + } + } + + private void handleBuildException(IResource pomFile, String type, Throwable ex) { + Throwable cause = getRootCause(ex); + // String msg = Messages.getString("plugin.markerBuildError", cause.getMessage()); //$NON-NLS-1$ + String msg = cause.getMessage(); + addMarker(pomFile, type, msg, 1, IMarker.SEVERITY_ERROR); +// console.logError(msg); + } + + private String getArtifactId(AbstractArtifactResolutionException rex) { + String id = rex.getGroupId() + ":" + rex.getArtifactId() + ":" + rex.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + if(rex.getClassifier() != null) { + id += ":" + rex.getClassifier(); //$NON-NLS-1$ + } + if(rex.getType() != null) { + id += ":" + rex.getType(); //$NON-NLS-1$ + } + return id; + } + + private String getErrorMessage(Throwable ex) { + return getRootCause(ex).getMessage(); + } + + private Throwable getRootCause(Throwable ex) { + Throwable lastCause = ex; + Throwable cause = lastCause.getCause(); + while(cause != null && cause != lastCause) { + if(cause instanceof ArtifactNotFoundException) { + cause = null; + } else { + lastCause = cause; + cause = cause.getCause(); + } + } + return cause == null ? lastCause : cause; + } + + + private void addErrorMarkers(IResource pomFile, String type, String msg, List<? extends Exception> exceptions) { + if(exceptions != null) { + for(Exception ex : exceptions) { + if(ex instanceof org.sonatype.aether.transfer.ArtifactNotFoundException) { + // ignored here, handled by addMissingArtifactMarkers + } else if(ex instanceof AbstractArtifactResolutionException) { + AbstractArtifactResolutionException rex = (AbstractArtifactResolutionException) ex; + String errorMessage = getArtifactId(rex) + " " + getErrorMessage(ex); //$NON-NLS-1$ + addMarker(pomFile, type, errorMessage, 1, IMarker.SEVERITY_ERROR); +// console.logError(errorMessage); + + } else { + addMarker(pomFile, type, ex.getMessage(), 1, IMarker.SEVERITY_ERROR); +// console.logError(msg + "; " + ex.toString()); + } + } + } + } + + public void deleteMarkers(IResource resource, String type) throws CoreException { + if (resource != null && resource.exists()) { + resource.deleteMarkers(type, true, IResource.DEPTH_INFINITE); + } + } + + private void addMissingArtifactMarkers(IResource pomFile, String type, MavenProject mavenProject) { +// Set<Artifact> directDependencies = mavenProject.getDependencyArtifacts(); + Set<Artifact> artifacts = mavenProject.getArtifacts(); + for(Artifact artifact : artifacts) { + if (!artifact.isResolved()) { + String errorMessage; +// if (directDependencies.contains(artifact)) { + errorMessage = NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_error_missing, artifact.toString()); +// } else { +// errorMessage = "Missing indirectly referenced artifact " + artifact.toString(); +// } + + if(mavenConfiguration.isOffline()) { + errorMessage = NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenMarkerManager_error_offline, errorMessage); + } + + addMarker(pomFile, type, errorMessage, 1, IMarker.SEVERITY_ERROR); + console.logError(errorMessage); + } + } + } + + public void addErrorMarkers(IResource resource, String type, Exception ex) { + Throwable cause = getRootCause(ex); + if(cause instanceof CoreException) { + CoreException cex = (CoreException) cause; + IStatus status = cex.getStatus(); + if(status != null) { + addMarker(resource, type, status.getMessage(), 1, IMarker.SEVERITY_ERROR, false /*isTransient*/); //$NON-NLS-1$ + IStatus[] children = status.getChildren(); + if(children != null) { + for(IStatus childStatus : children) { + addMarker(resource, type, childStatus.getMessage(), 1, IMarker.SEVERITY_ERROR, false /*isTransient*/); //$NON-NLS-1$ + } + } + } + } else { + addMarker(resource, type, cause.getMessage(), 1, IMarker.SEVERITY_ERROR, false /*isTransient*/); //$NON-NLS-1$ + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenProjectImportResult.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenProjectImportResult.java new file mode 100644 index 00000000..68d6a628 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MavenProjectImportResult.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.eclipse.core.resources.IProject; + +import org.eclipse.m2e.core.project.IMavenProjectImportResult; +import org.eclipse.m2e.core.project.MavenProjectInfo; + +public class MavenProjectImportResult implements IMavenProjectImportResult { + + private final IProject project; + private final MavenProjectInfo projectInfo; + + public MavenProjectImportResult(MavenProjectInfo projectInfo, IProject project) { + this.projectInfo = projectInfo; + this.project = project; + } + + public IProject getProject() { + return project; + } + + public MavenProjectInfo getMavenProjectInfo() { + return projectInfo; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MissingLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MissingLifecycleMapping.java new file mode 100644 index 00000000..cff8e77a --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MissingLifecycleMapping.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; + + +/** + * MissingLifecycleMapping + * + * @author igor + */ +public class MissingLifecycleMapping implements ILifecycleMapping { + + /** + * Lifecycle mapping id. Must match id of properties page defined in plugin.xml + */ + public static final String ID = "MISSING"; //$NON-NLS-1$ + + private final String missingMappingId; + + public MissingLifecycleMapping(String mappingId) { + this.missingMappingId = mappingId; + } + + public String getId() { + return ID; + } + + public String getName() { + return Messages.MissingLifecycleMapping_name; + } + + public List<String> getPotentialMojoExecutionsForBuildKind(IMavenProjectFacade projectFacade, int kind, + IProgressMonitor progressMonitor) { + return Collections.emptyList(); + } + + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) { + } + + public void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) { + } + + public List<AbstractBuildParticipant> getBuildParticipants(IMavenProjectFacade facade, IProgressMonitor monitor) { + return Collections.emptyList(); + } + + public List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade facade, IProgressMonitor monitor) { + return Collections.emptyList(); + } + + public String getMissingMappingId() { + return missingMappingId; + } + + public List<MojoExecution> getNotCoveredMojoExecutions(IMavenProjectFacade mavenProjectFacade, + IProgressMonitor monitor) throws CoreException { + return Collections.emptyList(); + } + + public boolean isInterestingPhase(String phase) { + return false; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionProjectConfigurator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionProjectConfigurator.java new file mode 100644 index 00000000..b71b4108 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionProjectConfigurator.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.MojoExecutionBuildParticipant; +import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; + + +/** + * MojoExecutionProjectConfigurator + * + * @author igor + */ +public class MojoExecutionProjectConfigurator extends AbstractProjectConfigurator { + + private final boolean runOnIncremental; + + public MojoExecutionProjectConfigurator(boolean runOnIncremental) { + this.runOnIncremental = runOnIncremental; + } + + protected MojoExecutionProjectConfigurator(String groupId, String artifactId, String versionRange, String goals, + boolean runOnIncremental) { + this.runOnIncremental = runOnIncremental; + + addPluginExecutionFilter(groupId, artifactId, versionRange, goals); + } + + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) { + // do nothing + } + + public AbstractBuildParticipant getBuildParticipant(MojoExecution execution) { + return new MojoExecutionBuildParticipant(execution, runOnIncremental); + } + + public static MojoExecutionProjectConfigurator fromString(String str, boolean runOnIncremental) { + if(str == null || str.trim().length() <= 0) { + return null; + } + + int p, c; + + p = 0; + c = nextColonIndex(str, p); + String groupId = substring(str, p, c); + + p = c + 1; + c = nextColonIndex(str, p); + String artifactId = substring(str, p, c); + + p = c + 1; + c = nextColonIndex(str, p); + String versionRange = substring(str, p, c); + + p = c + 1; + String goals = substring(str, p, str.length()); + + return new MojoExecutionProjectConfigurator(groupId, artifactId, versionRange, goals, runOnIncremental); + } + + private static String substring(String str, int start, int end) { + String substring = str.substring(start, end); + return "".equals(substring) ? null : substring; //$NON-NLS-1$ + } + + private static int nextColonIndex(String str, int pos) { + int idx = str.indexOf(':', pos); + if(idx < 0) { + throw new IllegalArgumentException("Invalid mojo execution template: " + str); + } + return idx; + } + + public String getName() { + return "execute"; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionUtils.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionUtils.java new file mode 100644 index 00000000..d6a5bda5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/MojoExecutionUtils.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import org.apache.maven.plugin.MojoExecution; + +/** + * MavenExecutionUtils + * + * @author mpoindexter + */ +public class MojoExecutionUtils { + public static String getExecutionKey(MojoExecution execution) { + return nvl(execution.getGroupId()) + ":" + nvl(execution.getArtifactId()) + ":" + nvl(execution.getVersion()) + ":" + nvl(execution.getGoal()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + private static String nvl(String s) { + return s == null ? "" : s; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ProjectConfigurationManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ProjectConfigurationManager.java new file mode 100644 index 00000000..1c48f23b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/ProjectConfigurationManager.java @@ -0,0 +1,716 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; + +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.IWorkingSet; + +import org.codehaus.plexus.util.StringUtils; + +import org.apache.maven.archetype.ArchetypeGenerationRequest; +import org.apache.maven.archetype.ArchetypeGenerationResult; +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.RemoteCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeManager; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.IMavenProjectImportResult; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.LocalProjectScanner; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectInfo; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.project.ResolverConfiguration; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; +import org.eclipse.m2e.core.util.Util; + +/** + * import Maven projects + * update project configuration from Maven + * enable Maven nature + * create new project + * + * @author igor + */ +public class ProjectConfigurationManager implements IProjectConfigurationManager, IMavenProjectChangedListener { + + final MavenConsole console; + + final MavenProjectManager projectManager; + + final MavenModelManager mavenModelManager; + + final IMavenMarkerManager mavenMarkerManager; + + final IMaven maven; + + final IMavenConfiguration mavenConfiguration; + + public ProjectConfigurationManager(IMaven maven, MavenConsole console, MavenProjectManager projectManager, + MavenModelManager mavenModelManager, IMavenMarkerManager mavenMarkerManager, IMavenConfiguration mavenConfiguration) { + this.console = console; + this.projectManager = projectManager; + this.mavenModelManager = mavenModelManager; + this.mavenMarkerManager = mavenMarkerManager; + this.maven = maven; + this.mavenConfiguration = mavenConfiguration; + } + + public List<IMavenProjectImportResult> importProjects(Collection<MavenProjectInfo> projectInfos, ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException { + long t1 = System.currentTimeMillis(); + + SubMonitor progress = SubMonitor.convert(monitor, Messages.ProjectConfigurationManager_task_importing, 100); + + ArrayList<IMavenProjectImportResult> result = new ArrayList<IMavenProjectImportResult>(); + ArrayList<IProject> projects = new ArrayList<IProject>(); + + SubMonitor subProgress = + SubMonitor.convert( progress.newChild( 10 ), projectInfos.size() * 100 ); + + // first, create all projects with basic configuration + for(MavenProjectInfo projectInfo : projectInfos) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + IProject project = create(projectInfo, configuration, subProgress.newChild(100)); + + result.add(new MavenProjectImportResult(projectInfo, project)); + + if (project != null) { + projects.add(project); + + addToWorkingSets(project, configuration.getWorkingSets()); + } + } + + hideNestedProjectsFromParents(projects); + // then configure maven for all projects + configureNewMavenProject(projects, progress.newChild(90)); + + long t2 = System.currentTimeMillis(); + console.logMessage("Project import completed " + ((t2 - t1) / 1000) + " sec"); + + return result; + } + + + private void setHidden(IResource resource) { + // Invoke IResource.setHidden() through reflection since it is only avaiable in Eclispe 3.4 & later + try { + Method m = IResource.class.getMethod("setHidden", boolean.class); //$NON-NLS-1$ + m.invoke(resource, Boolean.TRUE); + } catch (Exception ex) { + MavenLogger.log("Failed to hide resource; " + resource.getLocation().toOSString(), ex); + } + } + + private void hideNestedProjectsFromParents(List<IProject> projects) { + + if (!MavenPlugin.getDefault().getMavenConfiguration().isHideFoldersOfNestedProjects()) { + return; + } + // Prevent child project folders from showing up in parent project folders. + + Bundle bundle = ResourcesPlugin.getPlugin().getBundle(); + String version = (String) bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION); + Version currentVersion = org.osgi.framework.Version.parseVersion(version); + Version e34Version = new Version(3,4,0); + if (currentVersion.compareTo(e34Version) < 0) { + return; // IResource.setHidden doesn't exist in Eclipse prior to version 3.4 + } + HashMap<File, IProject> projectFileMap = new HashMap<File, IProject>(); + + for (IProject project: projects) { + projectFileMap.put(project.getLocation().toFile(), project); + } + for (IProject project: projects) { + File projectFile = project.getLocation().toFile(); + IProject physicalParentProject = projectFileMap.get(projectFile.getParentFile()); + if (physicalParentProject == null) { + continue; + } + IFolder folder = physicalParentProject.getFolder(projectFile.getName()); + if (folder.exists()) { + setHidden(folder); + } + } + } + + private void configureNewMavenProject(List<IProject> projects, IProgressMonitor monitor) + throws CoreException { + SubMonitor progress = SubMonitor.convert(monitor, Messages.ProjectConfigurationManager_task_configuring, 100); + + //SubProgressMonitor sub = new SubProgressMonitor(monitor, projects.size()+1); + + // first, resolve maven dependencies for all projects + MavenUpdateRequest updateRequest = new MavenUpdateRequest(mavenConfiguration.isOffline(), false); + for(IProject project : projects) { + updateRequest.addPomFile(project); + } + progress.subTask(Messages.ProjectConfigurationManager_task_refreshing); + projectManager.refresh(updateRequest, progress.newChild(75)); + + // TODO this emits project change events, which may be premature at this point + + //Creating maven facades + SubMonitor subProgress = SubMonitor.convert(progress.newChild(5), projects.size() * 100); + List<IMavenProjectFacade> facades = new ArrayList<IMavenProjectFacade>(projects.size()); + for(IProject project : projects) { + if(progress.isCanceled()) { + throw new OperationCanceledException(); + } + IMavenProjectFacade facade = projectManager.create(project, subProgress.newChild(100)); + if (facade != null) { + facades.add(facade); + } + } + + //MNGECLIPSE-1028 : Sort projects by build order here, + //as dependent projects need to be configured before depending projects (in WTP integration for ex.) + sortProjects(facades, progress.newChild(5)); + //Then, perform detailed project configuration + subProgress = SubMonitor.convert(progress.newChild(15), facades.size() * 100); + for(IMavenProjectFacade facade : facades) { + if(progress.isCanceled()) { + throw new OperationCanceledException(); + } + progress.subTask(NLS.bind(Messages.ProjectConfigurationManager_task_updating, facade.getProject().getName())); + MavenProject mavenProject = facade.getMavenProject(subProgress.newChild(5)); + MavenSession mavenSession = createMavenSession(facade, subProgress.newChild(5)); + ProjectConfigurationRequest request = new ProjectConfigurationRequest(facade, mavenProject, mavenSession, false /*updateSources*/); + updateProjectConfiguration(request, subProgress.newChild(90)); + } + } + + private MavenSession createMavenSession(IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException { + MavenExecutionRequest request = projectManager.createExecutionRequest(facade.getPom(), facade.getResolverConfiguration(), monitor); + return maven.createSession(request, facade.getMavenProject(monitor)); + } + + public void sortProjects(List<IMavenProjectFacade> facades, IProgressMonitor monitor) throws CoreException { + HashMap<MavenProject, IMavenProjectFacade> mavenProjectToFacadeMap = new HashMap<MavenProject, IMavenProjectFacade>(facades.size()); + for(IMavenProjectFacade facade:facades) { + mavenProjectToFacadeMap.put(facade.getMavenProject(monitor), facade); + } + facades.clear(); + for(MavenProject mavenProject: maven.getSortedProjects(new ArrayList<MavenProject>(mavenProjectToFacadeMap.keySet()))) { + facades.add(mavenProjectToFacadeMap.get(mavenProject)); + } + } + + // PlatformUI.getWorkbench().getWorkingSetManager().addToWorkingSets(project, new IWorkingSet[] {workingSet}); + private void addToWorkingSets(IProject project, IWorkingSet[] workingSets) { + if (workingSets != null && workingSets.length > 0) { + // IAdaptable[] elements = workingSet.adaptElements(new IAdaptable[] {project}); + // if(elements.length == 1) { + for (IWorkingSet workingSet : workingSets) { + IAdaptable[] oldElements = workingSet.getElements(); + IAdaptable[] newElements = new IAdaptable[oldElements.length + 1]; + System.arraycopy(oldElements, 0, newElements, 0, oldElements.length); + newElements[oldElements.length] = project; + + // Eclipse 3.2 compatibility + workingSet.setElements(Util.proxy(workingSet, A.class).adaptElements(newElements)); + // } + } + } + } + + /** + * A compatibility proxy stub + */ + private static interface A { + public IAdaptable[] adaptElements(IAdaptable[] objects); + } + + public void updateProjectConfiguration(IProject project, ResolverConfiguration configuration, IProgressMonitor monitor) throws CoreException { + IFile pom = project.getFile(IMavenConstants.POM_FILE_NAME); + if (pom.isAccessible()) { + projectManager.refresh(new MavenUpdateRequest(project, mavenConfiguration.isOffline(), false), monitor); + IMavenProjectFacade facade = projectManager.create(pom, false, monitor); + if (facade != null) { // facade is null if pom.xml cannot be read + ProjectConfigurationRequest request = new ProjectConfigurationRequest(facade, facade.getMavenProject(monitor), createMavenSession(facade, monitor), true /*updateSources*/); + updateProjectConfiguration(request, monitor); + } + } + } + + private void updateProjectConfiguration(ProjectConfigurationRequest request, + IProgressMonitor monitor) throws CoreException { + IProject project = request.getProject(); + addMavenNature(project, monitor); + + IMavenProjectFacade mavenProjectFacade = request.getMavenProjectFacade(); + validateProjectConfiguration(mavenProjectFacade, monitor); + + ILifecycleMapping lifecycleMapping = getLifecycleMapping(mavenProjectFacade, monitor); + lifecycleMapping.configure(request, monitor); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.core.project.IProjectConfigurationManager#validateProjectConfiguration(org.eclipse.m2e.core.project.IMavenProjectFacade, org.eclipse.core.runtime.IProgressMonitor) + */ + public boolean validateProjectConfiguration(IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) { + try { + mavenProjectFacade.setHasValidConfiguration(false); + mavenMarkerManager.deleteMarkers(mavenProjectFacade.getPom(), IMavenConstants.MARKER_CONFIGURATION_ID); + + ILifecycleMapping lifecycleMapping = getLifecycleMapping(mavenProjectFacade, monitor); + if(lifecycleMapping == null || lifecycleMapping instanceof MissingLifecycleMapping) { + String lifecycleId = null; + if(lifecycleMapping != null) { + lifecycleId = lifecycleMapping.getId(); + } + mavenMarkerManager.addMarker(mavenProjectFacade.getPom(), IMavenConstants.MARKER_CONFIGURATION_ID, + NLS.bind(Messages.LifecycleMissing, lifecycleId, mavenProjectFacade.getPackaging()), 1 /*lineNumber*/, + IMarker.SEVERITY_ERROR); + return false; + } + + List<MojoExecution> notCoveredMojoExecutions = lifecycleMapping.getNotCoveredMojoExecutions(mavenProjectFacade, + monitor); + if(notCoveredMojoExecutions != null && notCoveredMojoExecutions.size() != 0) { + for(MojoExecution mojoExecution : notCoveredMojoExecutions) { + mavenMarkerManager.addMarker( + mavenProjectFacade.getPom(), + IMavenConstants.MARKER_CONFIGURATION_ID, + NLS.bind(Messages.LifecycleConfigurationMojoExecutionNotCovered, mojoExecution.toString(), + mojoExecution.getLifecyclePhase()), 1 /*lineNumber*/, IMarker.SEVERITY_ERROR); + } + return false; + } + mavenProjectFacade.setHasValidConfiguration(true); + return true; + } catch(CoreException e) { + mavenMarkerManager.addErrorMarkers(mavenProjectFacade.getPom(), IMavenConstants.MARKER_CONFIGURATION_ID, e); + return false; + } + } + + public void enableMavenNature(IProject project, ResolverConfiguration configuration, IProgressMonitor monitor) + throws CoreException { + monitor.subTask(Messages.ProjectConfigurationManager_task_enable_nature); + enableBasicMavenNature(project, configuration, monitor); + + ArrayList<IProject> projects = new ArrayList<IProject>(); + projects.add(project); + configureNewMavenProject(projects, monitor); + } + + private void enableBasicMavenNature(IProject project, ResolverConfiguration configuration, IProgressMonitor monitor) + throws CoreException { + projectManager.setResolverConfiguration(project, configuration); + + // add maven nature even for projects without valid pom.xml file + addMavenNature(project, monitor); + } + + private void addMavenNature(IProject project, IProgressMonitor monitor) throws CoreException { + if (!project.hasNature(IMavenConstants.NATURE_ID)) { + IProjectDescription description = project.getDescription(); + String[] prevNatures = description.getNatureIds(); + String[] newNatures = new String[prevNatures.length + 1]; + System.arraycopy(prevNatures, 0, newNatures, 1, prevNatures.length); + newNatures[0] = IMavenConstants.NATURE_ID; + description.setNatureIds(newNatures); + project.setDescription(description, monitor); + } + } + + public void disableMavenNature(IProject project, IProgressMonitor monitor) throws CoreException { + monitor.subTask(Messages.ProjectConfigurationManager_task_disable_nature); + + IMavenProjectFacade facade = projectManager.create(project, monitor); + if(facade!=null) { + ILifecycleMapping lifecycleMapping = getLifecycleMapping(facade, monitor); + ProjectConfigurationRequest request = new ProjectConfigurationRequest(facade, facade.getMavenProject(monitor), createMavenSession(facade, monitor), false /*updateSources*/); + lifecycleMapping.unconfigure(request, monitor); + } + + project.deleteMarkers(IMavenConstants.MARKER_ID, true, IResource.DEPTH_INFINITE); + + IProjectDescription description = project.getDescription(); + ArrayList<String> newNatures = new ArrayList<String>(); + for(String natureId : description.getNatureIds()) { + if(!IMavenConstants.NATURE_ID.equals(natureId)) { + newNatures.add(natureId); + } + } + description.setNatureIds(newNatures.toArray(new String[newNatures.size()])); + ArrayList<ICommand> newCommands = new ArrayList<ICommand>(); + for (ICommand command : description.getBuildSpec()) { + if (!IMavenConstants.BUILDER_ID.equals(command.getBuilderName())) { + newCommands.add(command); + } + } + description.setBuildSpec(newCommands.toArray(new ICommand[newCommands.size()])); + project.setDescription(description, null); + } + + // project creation + + /** + * Creates simple Maven project + * <p> + * The following steps are executed in the given order: + * <ul> + * <li>Creates the workspace project</li> + * <li>Adds project to working set</li> + * <li>Creates the required folders</li> + * <li>Creates the POM</li> + * <li>Configures project</li> + * </ul> + * </p> + */ + // XXX should use Maven plugin configurations instead of manually specifying folders + public void createSimpleProject(IProject project, IPath location, Model model, String[] directories, + ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException { + String projectName = project.getName(); + monitor.beginTask(NLS.bind(Messages.ProjectConfigurationManager_task_creating, projectName), 5); + + monitor.subTask(Messages.ProjectConfigurationManager_task_creating_workspace); + IProjectDescription description = ResourcesPlugin.getWorkspace().newProjectDescription(projectName); + description.setLocation(location); + project.create(description, monitor); + project.open(monitor); + monitor.worked(1); + + addToWorkingSets(project, configuration.getWorkingSets()); + monitor.worked(1); + + monitor.subTask(Messages.ProjectConfigurationManager_task_creating_pom); + IFile pomFile = project.getFile(IMavenConstants.POM_FILE_NAME); + mavenModelManager.createMavenModel(pomFile, model); + monitor.worked(1); + + monitor.subTask(Messages.ProjectConfigurationManager_task_creating_folders); + for(int i = 0; i < directories.length; i++ ) { + Util.createFolder(project.getFolder(directories[i]), false); + } + monitor.worked(1); + + monitor.subTask(Messages.ProjectConfigurationManager_task_creating_project); + enableMavenNature(project, configuration.getResolverConfiguration(), monitor); + monitor.worked(1); + } + + /** + * Creates project structure using Archetype and then imports created project + */ + public void createArchetypeProject(IProject project, IPath location, Archetype archetype, String groupId, + String artifactId, String version, String javaPackage, Properties properties, + ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException { + monitor.beginTask(NLS.bind(Messages.ProjectConfigurationManager_task_creating_project1, project.getName()), 2); + + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + + monitor.subTask(NLS.bind(Messages.ProjectConfigurationManager_task_executing_archetype, archetype.getGroupId(), archetype.getArtifactId())); + if(location == null) { + // if the project should be created in the workspace, figure out the path + location = workspaceRoot.getLocation(); + } + + try { + + + + Artifact artifact = resolveArchetype(archetype, monitor); + + ArchetypeGenerationRequest request = new ArchetypeGenerationRequest() // + .setTransferListener(maven.createTransferListener(monitor)) // + .setArchetypeGroupId(artifact.getGroupId()) // + .setArchetypeArtifactId(artifact.getArtifactId()) // + .setArchetypeVersion(artifact.getVersion()) // + .setArchetypeRepository(archetype.getRepository()) // + .setGroupId(groupId) // + .setArtifactId(artifactId) // + .setVersion(version) // + .setPackage(javaPackage) // the model does not have a package field + .setLocalRepository(maven.getLocalRepository()) // + .setRemoteArtifactRepositories(maven.getArtifactRepositories(true)) + .setProperties(properties).setOutputDirectory(location.toPortableString()); + + MavenSession session = maven.createSession(maven.createExecutionRequest(monitor), null); + + MavenSession oldSession = MavenPlugin.getDefault().setSession(session); + + ArchetypeGenerationResult result; + try { + result = getArchetyper().generateProjectFromArchetype(request); + } finally { + MavenPlugin.getDefault().setSession(oldSession); + } + + Exception cause = result.getCause(); + if(cause != null) { + String msg = NLS.bind(Messages.ProjectConfigurationManager_error_unable_archetype, archetype.toString()); + MavenLogger.log(msg, cause); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg, cause)); + } + monitor.worked(1); + + // XXX Archetyper don't allow to specify project folder + String projectFolder = location.append(artifactId).toFile().getAbsolutePath(); + + LocalProjectScanner scanner = new LocalProjectScanner(workspaceRoot.getLocation().toFile(), // + projectFolder, true, mavenModelManager, console); + scanner.run(monitor); + + Set<MavenProjectInfo> projectSet = collectProjects(scanner.getProjects()); + + importProjects(projectSet, configuration, monitor); + + monitor.worked(1); + } catch (CoreException e) { + throw e; + } catch (InterruptedException e) { + throw new CoreException(Status.CANCEL_STATUS); + } catch (Exception ex) { + throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.m2e", Messages.ProjectConfigurationManager_error_failed, ex)); //$NON-NLS-1$ + } + } + + /** + * Apparently, Archetype#generateProjectFromArchetype 2.0-alpha-4 does not attempt to resolve archetype + * from configured remote repositories. To compensate, we populate local repo with archetype pom/jar. + */ + private Artifact resolveArchetype(Archetype a, IProgressMonitor monitor) throws CoreException { + ArrayList<ArtifactRepository> repos = new ArrayList<ArtifactRepository>(); + repos.addAll(maven.getArtifactRepositories()); // see org.apache.maven.archetype.downloader.DefaultDownloader#download + + //MNGECLIPSE-1399 use archetype repository too, not just the default ones + String artifactRemoteRepository = a.getRepository(); + + try { + + if (StringUtils.isBlank(artifactRemoteRepository)){ + + IMavenConfiguration mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + if (!mavenConfiguration.isOffline()){ + //Try to find the repository from remote catalog if needed + final ArchetypeManager archetypeManager = MavenPlugin.getDefault().getArchetypeManager(); + RemoteCatalogFactory factory = archetypeManager.findParentCatalogFactory(a, RemoteCatalogFactory.class); + if (factory != null) { + //Grab the computed remote repository url + artifactRemoteRepository = factory.getRepositoryUrl(); + a.setRepository(artifactRemoteRepository);//Hopefully will prevent further lookups for the same archetype + } + } + } + + if (StringUtils.isNotBlank(artifactRemoteRepository)) { + ArtifactRepository archetypeRepository = maven.createArtifactRepository("archetype", a.getRepository().trim()); //$NON-NLS-1$ + repos.add(0,archetypeRepository);//If the archetype doesn't exist locally, this will be the first remote repo to be searched. + } + + maven.resolve(a.getGroupId(), a.getArtifactId(),a.getVersion(), "pom", null, repos, monitor); //$NON-NLS-1$ + return maven.resolve(a.getGroupId(), a.getArtifactId(),a.getVersion(), "jar", null, repos, monitor); //$NON-NLS-1$ + } catch (CoreException e) { + StringBuilder sb = new StringBuilder(); + sb.append(Messages.ProjectConfigurationManager_error_resolve).append(a.getGroupId()).append(':').append(a.getArtifactId()).append(':').append(a.getVersion()); + sb.append(Messages.ProjectConfigurationManager_error_resolve2); + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, sb.toString(), e)); + } + } + + private org.apache.maven.archetype.Archetype getArchetyper() { + return MavenPlugin.getDefault().getArchetype(); + } + + public Set<MavenProjectInfo> collectProjects(Collection<MavenProjectInfo> projects) { + + // TODO what does this do? + return new LinkedHashSet<MavenProjectInfo>() { + private static final long serialVersionUID = 1L; + + public Set<MavenProjectInfo> collectProjects(Collection<MavenProjectInfo> projects) { + for(MavenProjectInfo projectInfo : projects) { + console.logMessage("Collecting project info " + projectInfo); + add(projectInfo); + collectProjects(projectInfo.getProjects()); + } + return this; + } + }.collectProjects(projects); + } + + public ISchedulingRule getRule() { + return ResourcesPlugin.getWorkspace().getRuleFactory().buildRule(); + } + + private IProject create(MavenProjectInfo projectInfo, ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + + File pomFile = projectInfo.getPomFile(); + Model model = projectInfo.getModel(); + if(model == null) { + model = maven.readModel(pomFile); + projectInfo.setModel(model); + } + + String projectName = configuration.getProjectName(model); + + File projectDir = pomFile.getParentFile(); + String projectParent = projectDir.getParentFile().getAbsolutePath(); + + if (projectInfo.getBasedirRename() == MavenProjectInfo.RENAME_REQUIRED) { + File newProject = new File(projectDir.getParent(), projectName); + if(!projectDir.equals(newProject)) { + boolean renamed = projectDir.renameTo(newProject); + if(!renamed) { + StringBuilder msg = new StringBuilder(); + msg.append(NLS.bind(Messages.ProjectConfigurationManager_error_rename, projectDir.getAbsolutePath())).append('.'); + if (newProject.exists()) { + msg.append(NLS.bind(Messages.ProjectConfigurationManager_error_targetDir, newProject.getAbsolutePath())); + } + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, msg.toString(), null)); + } + projectInfo.setPomFile(getCanonicalPomFile(newProject)); + projectDir = newProject; + } + } else { + if(projectParent.equals(root.getLocation().toFile().getAbsolutePath())) { + // immediately under workspace root, project name must match filesystem directory name + projectName = projectDir.getName(); + } + } + + monitor.subTask(NLS.bind(Messages.ProjectConfigurationManager_task_importing2, projectName)); + + IProject project = root.getProject(projectName); + if(project.exists()) { + console.logError("Project " + projectName + " already exists"); + return null; + } + + if(projectDir.equals(root.getLocation().toFile())) { + console.logError("Can't create project " + projectName + " at Workspace folder"); + return null; + } + + if(projectParent.equals(root.getLocation().toFile().getAbsolutePath())) { + project.create(monitor); + } else { + IProjectDescription description = workspace.newProjectDescription(projectName); + description.setLocation(new Path(projectDir.getAbsolutePath())); + project.create(description, monitor); + } + + if(!project.isOpen()) { + project.open(monitor); + } + + ResolverConfiguration resolverConfiguration = configuration.getResolverConfiguration(); + enableBasicMavenNature(project, resolverConfiguration, monitor); + + return project; + } + + private File getCanonicalPomFile(File projectDir) throws CoreException { + try { + return new File(projectDir.getCanonicalFile(), IMavenConstants.POM_FILE_NAME); + } catch(IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, // + NLS.bind(Messages.ProjectConfigurationManager_0, projectDir.getAbsolutePath()), null)); + } + } + + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + for(MavenProjectChangedEvent event : events) { + try { + ILifecycleMapping lifecycleMapping = getLifecycleMapping(event.getMavenProject(), monitor); + if(lifecycleMapping != null) { + for(AbstractProjectConfigurator configurator : lifecycleMapping.getProjectConfigurators(event.getMavenProject(), monitor)) { + //MNGECLIPSE-2004 : only send the relevant event to the configurator + configurator.mavenProjectChanged(event, monitor); + } + } + } catch (CoreException e) { + MavenLogger.log(e); + } + } + } + + public ILifecycleMapping getLifecycleMapping(IMavenProjectFacade projectFacade, IProgressMonitor monitor) throws CoreException { + if (projectFacade==null) { + return null; + } + + return projectFacade.getLifecycleMapping(monitor); + } + + public ILifecycleMapping getDefaultLifecycleMapping(IMavenProjectFacade projectFacade, IProgressMonitor monitor) + throws CoreException { + if(projectFacade == null) { + return null; + } + + return projectFacade.getLifecycleMapping(monitor); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/WorkspaceStateWriter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/WorkspaceStateWriter.java new file mode 100644 index 00000000..a2f84921 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/WorkspaceStateWriter.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Properties; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.Artifact; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectManager; + +/** + * Maintains map file of maven artifacts present in workspace. + */ +public class WorkspaceStateWriter implements IMavenProjectChangedListener { + + private MavenProjectManager projectManager; + + public WorkspaceStateWriter(MavenProjectManager projectManager) { + this.projectManager = projectManager; + } + + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + try { + Properties state = new Properties(); + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + for(IMavenProjectFacade projectFacade : projectManager.getProjects()) { + try { + Artifact artifact = projectFacade.getMavenProject(monitor).getArtifact(); + IFile pomFile = projectFacade.getPom(); + IPath location = pomFile.getLocation(); + if(location != null) { + File pom = location.toFile(); + if(pom.canRead()) { + String key = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":pom:" + artifact.getBaseVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + state.put(key, pom.getCanonicalPath()); + } + } + IResource outputLocation = root.findMember(projectFacade.getOutputLocation()); + if (!"pom".equals(artifact.getType()) && outputLocation != null && outputLocation.exists()) { //$NON-NLS-1$ + String key = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() + ":" + artifact.getBaseVersion(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + state.put(key, outputLocation.getLocation().toFile().getCanonicalPath()); + } + } catch (CoreException ex) { + MavenLogger.log("Error writing workspace state file", ex); + } + } + + OutputStream buf = new BufferedOutputStream(new FileOutputStream(projectManager.getWorkspaceStateFile())); + try { + state.store(buf, null); + } finally { + buf.close(); + } + + } catch(IOException ex) { + MavenLogger.log("Error writing workspace state file", ex); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/AbstractMavenDependencyResolver.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/AbstractMavenDependencyResolver.java new file mode 100644 index 00000000..819fd46a --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/AbstractMavenDependencyResolver.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.execution.MavenExecutionRequest; + +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * AbstractMavenDependencyResolver + * + * @author igor + */ +public abstract class AbstractMavenDependencyResolver { + + private ProjectRegistryManager manager; + private MutableProjectRegistry contextRegistry; + + protected IMaven getMaven() { + return manager.getMaven(); + } + + final void setManager(ProjectRegistryManager manager) { + this.manager = manager; + } + + protected ProjectRegistryManager getManager() { + return manager; + } + + public abstract void resolveProjectDependencies(IMavenProjectFacade facade, MavenExecutionRequest mavenRequest, Set<Capability> capabilities, Set<RequiredCapability> requirements, IProgressMonitor monitor) throws CoreException; + + void setContextProjectRegistry(MutableProjectRegistry contextRegistry) { + this.contextRegistry = contextRegistry; + } + + protected List<MavenProjectFacade> getProjects() { + return Arrays.asList(contextRegistry.getProjects()); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/BasicProjectRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/BasicProjectRegistry.java new file mode 100644 index 00000000..5ab82a63 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/BasicProjectRegistry.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * Registry of all workspace projects and their dependencies. + * + * @author igor + */ +abstract class BasicProjectRegistry implements Serializable { + + private static final long serialVersionUID = 5542512601401896748L; + + private final String m2e_version = MavenPlugin.getQualifiedVersion(); + + /** + * Map<ArtifactKey, IPath> Maps ArtifactKey to full workspace IPath of the POM file that defines this artifact. + */ + protected final Map<ArtifactKey, IFile> workspaceArtifacts = new HashMap<ArtifactKey, IFile>(); + + /** + * Maps full pom IPath to MavenProjectFacade + */ + protected final Map<IFile, MavenProjectFacade> workspacePoms = new HashMap<IFile, MavenProjectFacade>(); + + /** + * Maps required capabilities to projects that require them. + */ + protected final Map<VersionlessKey, Map<RequiredCapability, Set<IFile>>> requiredCapabilities = new HashMap<VersionlessKey, Map<RequiredCapability, Set<IFile>>>(); + + /** + * Maps project pom.xml file to the capabilities provided by the project + */ + protected final Map<IFile, Set<Capability>> projectCapabilities = new HashMap<IFile, Set<Capability>>(); + + /** + * Maps project pom.xml file to the capabilities required by the project + */ + protected final Map<IFile, Set<RequiredCapability>> projectRequirements = new HashMap<IFile, Set<RequiredCapability>>(); + + protected BasicProjectRegistry() { + } + + protected BasicProjectRegistry(BasicProjectRegistry other) { + replaceWith(other); + } + + protected final void replaceWith(BasicProjectRegistry other) { + clear(); + + copy(other.workspaceArtifacts, workspaceArtifacts); + copy(other.workspacePoms, workspacePoms); + copy(other.projectCapabilities, projectCapabilities); + copy(other.projectRequirements, projectRequirements); + copy(other.requiredCapabilities, requiredCapabilities); + } + + /** + * THIS IS NOT A GENERIC DEEP COPY IMPLEMENTATION! + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private static void copy(Map from, Map to) { + for(Map.Entry entry : (Set<Map.Entry>) from.entrySet()) { + Object value = entry.getValue(); + if(value instanceof Map) { + Map map = new HashMap(); + copy((Map) value, map); + value = map; + } else if(value instanceof Set) { + Set set = new HashSet((Set) value); + value = set; + } + to.put(entry.getKey(), value); + } + } + + public MavenProjectFacade getProjectFacade(IFile pom) { + return workspacePoms.get(pom); + } + + public MavenProjectFacade getProjectFacade(String groupId, String artifactId, String version) { + IFile path = workspaceArtifacts.get(new ArtifactKey(groupId, artifactId, version, null)); + if(path == null) { + return null; + } + return workspacePoms.get(path); + } + + /** + * @TODO return a List + */ + public MavenProjectFacade[] getProjects() { + return workspacePoms.values().toArray(new MavenProjectFacade[workspacePoms.size()]); + } + + public IFile getWorkspaceArtifact(ArtifactKey key) { + return workspaceArtifacts.get(key); + } + + protected void clear() { + workspaceArtifacts.clear(); + workspacePoms.clear(); + requiredCapabilities.clear(); + projectCapabilities.clear(); + projectRequirements.clear(); + } + + public boolean isValid() { + return MavenPlugin.getQualifiedVersion().equals(m2e_version) // + && workspaceArtifacts != null // + && workspacePoms != null // + && requiredCapabilities != null // + && projectCapabilities != null // + && projectRequirements != null // + && areFacadesValid(); + } + + private boolean areFacadesValid() { + for(MavenProjectFacade facade : workspacePoms.values()) { + if(facade == null || facade.getPom() == null || facade.getPom().getLocation() == null) { + return false; + } + } + return true; + } + + protected Set<RequiredCapability> getProjectRequirements(IFile pom) { + return projectRequirements.get(pom); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/Capability.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/Capability.java new file mode 100644 index 00000000..6b7f767f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/Capability.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.Serializable; + + +/** + * Capability + * + * @author igor + */ +public abstract class Capability implements Serializable { + + private static final long serialVersionUID = 6057170997402911788L; + + private final VersionlessKey versionlessKey; + + public Capability(String namespace, String id) { + if(namespace == null || id == null) { + throw new NullPointerException(); + } + this.versionlessKey = new VersionlessKey(namespace, id); + } + + public VersionlessKey getVersionlessKey() { + return versionlessKey; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/DefaultMavenDependencyResolver.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/DefaultMavenDependencyResolver.java new file mode 100644 index 00000000..c27b325b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/DefaultMavenDependencyResolver.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.DependencyResolutionResult; +import org.apache.maven.project.MavenProject; + +import org.sonatype.aether.graph.Dependency; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * DefaultMavenDependencyResolver + * + * @author igor + */ +public class DefaultMavenDependencyResolver extends AbstractMavenDependencyResolver { + private final IMavenMarkerManager markerManager; + + public DefaultMavenDependencyResolver(ProjectRegistryManager manager, IMavenMarkerManager markerManager) { + setManager(manager); + this.markerManager = markerManager; + } + + public void resolveProjectDependencies(IMavenProjectFacade facade, MavenExecutionRequest mavenRequest, + Set<Capability> capabilities, Set<RequiredCapability> requirements, IProgressMonitor monitor) + throws CoreException { + markerManager.deleteMarkers(facade.getPom(), IMavenConstants.MARKER_DEPENDENCY_ID); + + MavenExecutionResult mavenResult = getMaven().readProject(mavenRequest, monitor); + + markerManager.addMarkers(facade.getPom(), IMavenConstants.MARKER_DEPENDENCY_ID, mavenResult); + + if(!facade.getResolverConfiguration().shouldResolveWorkspaceProjects()) { + return; + } + + MavenProject mavenProject = facade.getMavenProject(); + + // dependencies + + // parent + Artifact parentArtifact = mavenProject.getParentArtifact(); + if(parentArtifact != null) { + requirements.add(MavenRequiredCapability.createMavenParent(new ArtifactKey(parentArtifact))); + } + + // resolved dependencies + for(Artifact artifact : mavenProject.getArtifacts()) { + requirements.add(MavenRequiredCapability.createMaven(new ArtifactKey(artifact), artifact.getScope(), + artifact.isOptional())); + } + + // extension plugins (affect packaging type calculation) + for(Plugin plugin : mavenProject.getBuildPlugins()) { + if(plugin.isExtensions()) { + ArtifactKey artifactKey = new ArtifactKey(plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion(), + null); + requirements.add(MavenRequiredCapability.createMaven(artifactKey, "plugin", false)); //$NON-NLS-1$ + } + } + + // missing dependencies + DependencyResolutionResult resolutionResult = mavenResult.getDependencyResolutionResult(); + if(resolutionResult != null && resolutionResult.getUnresolvedDependencies() != null) { + for(Dependency dependency : resolutionResult.getUnresolvedDependencies()) { + org.sonatype.aether.artifact.Artifact artifact = dependency.getArtifact(); + ArtifactKey dependencyKey = new ArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), + artifact.getVersion(), null); + requirements.add(MavenRequiredCapability.createMaven(dependencyKey, dependency.getScope(), + dependency.isOptional())); + } + } + + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipsePluginDependenciesResolver.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipsePluginDependenciesResolver.java new file mode 100644 index 00000000..db507922 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipsePluginDependenciesResolver.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.util.List; + +import org.codehaus.plexus.component.annotations.Component; + +import org.apache.maven.model.Plugin; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver; +import org.apache.maven.plugin.internal.PluginDependenciesResolver; + +import org.sonatype.aether.RepositorySystemSession; +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.graph.DependencyFilter; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.repository.RemoteRepository; + + +@Component(role = PluginDependenciesResolver.class) +public class EclipsePluginDependenciesResolver extends DefaultPluginDependenciesResolver { + + /* + * Plugin realms are cached and there is currently no way to purge cached + * realms due to http://jira.codehaus.org/browse/MNG-4194. + * + * Workspace plugins cannot be cached, so we disable this until MNG-4194 is fixed. + * + * Corresponding m2e JIRA https://issues.sonatype.org/browse/MNGECLIPSE-1448 + */ + + @Override + public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session) + throws PluginResolutionException { + boolean disabled = EclipseWorkspaceArtifactRepository.isDisabled(); + EclipseWorkspaceArtifactRepository.setDisabled(true); + try { + return super.resolve(plugin, repositories, session); + } finally { + EclipseWorkspaceArtifactRepository.setDisabled(disabled); + } + } + + @Override + public DependencyNode resolve(Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter, + List<RemoteRepository> repositories, RepositorySystemSession session) throws PluginResolutionException { + boolean disabled = EclipseWorkspaceArtifactRepository.isDisabled(); + EclipseWorkspaceArtifactRepository.setDisabled(true); + try { + return super.resolve(plugin, pluginArtifact, dependencyFilter, repositories, session); + } finally { + EclipseWorkspaceArtifactRepository.setDisabled(disabled); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipseWorkspaceArtifactRepository.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipseWorkspaceArtifactRepository.java new file mode 100644 index 00000000..c38a1c89 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/EclipseWorkspaceArtifactRepository.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; + +import org.apache.maven.repository.LocalArtifactRepository; + +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.repository.WorkspaceReader; +import org.sonatype.aether.repository.WorkspaceRepository; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +public final class EclipseWorkspaceArtifactRepository extends LocalArtifactRepository implements WorkspaceReader { + + private static final long serialVersionUID = 1018465082844566543L; + + private final transient ProjectRegistryManager.Context context; + + private static final ThreadLocal<Boolean> disabled = new ThreadLocal<Boolean>(); + + private WorkspaceRepository workspaceRepository; + + public EclipseWorkspaceArtifactRepository(ProjectRegistryManager.Context context) { + this.context = context; + this.workspaceRepository = new WorkspaceRepository("ide", getClass()); //$NON-NLS-1$ + } + + protected File resolveAsEclipseProject(String groupId, String artifactId, String baseVersion, String classifier, String extension) { + if (isDisabled()) { + return null; + } + + if(context == null) { // XXX this is actually a bug + return null; + } + + // check in the workspace, note that workspace artifacts never have classifiers + ArtifactKey key = new ArtifactKey(groupId, artifactId, baseVersion, null); + IFile pom = context.state.getWorkspaceArtifact(key); + if(pom == null || !pom.isAccessible()) { + return null; + } + if(context.pom != null && pom.equals(context.pom)) { + return null; + } + +// if(!"pom".equals(artifact.getType())) { +// return false; +// } + + if(context.resolverConfiguration.shouldResolveWorkspaceProjects()) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath file = pom.getLocation(); + if (!"pom".equals(extension)) { //$NON-NLS-1$ + MavenProjectFacade facade = context.state.getProjectFacade(pom); + IFolder outputLocation = root.getFolder(facade.getOutputLocation()); + if (outputLocation.exists()) { + file = outputLocation.getLocation(); + } + } + + return file.toFile(); + } + + return null; + } + + public File findArtifact(Artifact artifact) { + return resolveAsEclipseProject(artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion(), + artifact.getClassifier(), artifact.getExtension()); + } + + public org.apache.maven.artifact.Artifact find(org.apache.maven.artifact.Artifact artifact) { + File file = resolveAsEclipseProject(artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion(), + artifact.getClassifier(), artifact.getType()); + + if(file != null) { + artifact.setFile(file); + artifact.setResolved(true); + } + + return artifact; + } + + public boolean hasLocalMetadata() { + return false; // XXX + } + + public static void setDisabled(boolean disable) { + disabled.set(disable? Boolean.TRUE: null); + } + + public static boolean isDisabled() { + return Boolean.TRUE.equals(disabled.get()); + } + + public int hashCode() { + return 0; // no state + } + + public boolean equals(Object obj) { + return obj instanceof EclipseWorkspaceArtifactRepository; + } + + public List<String> findVersions(Artifact artifact) { + return findVersions(artifact.getGroupId(), artifact.getArtifactId()); + } + + @Override + public List<String> findVersions(org.apache.maven.artifact.Artifact artifact) { + return findVersions(artifact.getGroupId(), artifact.getArtifactId()); + } + + private List<String> findVersions(String groupId, String artifactId) { + ArrayList<String> versions = new ArrayList<String>(); + + if (isDisabled()) { + return versions; + } + + if(context == null) { // XXX this is actually a bug + return versions; + } + + for (MavenProjectFacade facade : context.state.getProjects()) { + ArtifactKey artifactKey = facade.getArtifactKey(); + if (groupId.equals(artifactKey.getGroupId()) && artifactId.equals(artifactKey.getArtifactId())) { + versions.add(artifactKey.getVersion()); + } + } + + return versions; + } + + public WorkspaceRepository getRepository() { + return workspaceRepository; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ILifecycleMapping2.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ILifecycleMapping2.java new file mode 100644 index 00000000..2b3d1960 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ILifecycleMapping2.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + +/** + * ILifecycleMapping2 + * + * @author igor + */ +public interface ILifecycleMapping2 extends ILifecycleMapping { + public AbstractMavenDependencyResolver getDependencyResolver(IProgressMonitor monitor); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/IProjectRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/IProjectRegistry.java new file mode 100644 index 00000000..dc4c605d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/IProjectRegistry.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * Registry of all known workspace maven projects. + * + * @author igor + */ +public interface IProjectRegistry { + + public MavenProjectFacade getProjectFacade(IFile pom); + + public MavenProjectFacade getProjectFacade(String groupId, String artifactId, String version); + + public MavenProjectFacade[] getProjects(); + + public IFile getWorkspaceArtifact(ArtifactKey key); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenCapability.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenCapability.java new file mode 100644 index 00000000..445fb35e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenCapability.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * MavenCapability + * + * @author igor + */ +public class MavenCapability extends Capability { + + private static final long serialVersionUID = 8930981127331238566L; + + /** + * Regular Maven dependency as defined in <dependency/> pom.xml element. + */ + public static final String NS_MAVEN_ARTIFACT = "maven-artifact"; //$NON-NLS-1$ + + /** + * Maven parent dependency as defined in <parent/> pom.xml element. + */ + public static final String NS_MAVEN_PARENT = "maven-parent"; //$NON-NLS-1$ + + private final String version; + + private MavenCapability(String namespace, String id, String version) { + super(namespace, id); + this.version = version; + } + + public String getVersion() { + return version; + } + + public String toString() { + return getVersionlessKey().toString() + "/" + version; //$NON-NLS-1$ + } + + public int hashCode() { + int hash = getVersionlessKey().hashCode(); + hash = hash * 17 + version.hashCode(); + return hash; + } + + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(!(obj instanceof MavenCapability)) { + return false; + } + MavenCapability other = (MavenCapability) obj; + return getVersionlessKey().equals(other.getVersionlessKey()) && version.equals(other.version); + } + + public static MavenCapability createMaven(ArtifactKey key) { + return new MavenCapability(NS_MAVEN_ARTIFACT, getId(key), key.getVersion()); + } + + public static MavenCapability createMavenParent(ArtifactKey key) { + return new MavenCapability(NS_MAVEN_PARENT, getId(key), key.getVersion()); + } + + static String getId(ArtifactKey key) { + StringBuilder sb = new StringBuilder(); + sb.append(key.getGroupId()); + sb.append(':').append(key.getArtifactId()); + if(key.getClassifier() != null) { + sb.append(':').append(key.getClassifier()); + } + return sb.toString(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenProjectFacade.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenProjectFacade.java new file mode 100644 index 00000000..28dbf8f3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenProjectFacade.java @@ -0,0 +1,382 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.File; +import java.io.Serializable; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ArtifactRef; +import org.eclipse.m2e.core.embedder.ArtifactRepositoryRef; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.IMavenProjectVisitor; +import org.eclipse.m2e.core.project.IMavenProjectVisitor2; +import org.eclipse.m2e.core.project.MavenProjectUtils; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.project.ResolverConfiguration; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + + +/** + * MavenProject facade + */ +public class MavenProjectFacade implements IMavenProjectFacade, Serializable { + + private static final long serialVersionUID = 707484407691175077L; + + private final ProjectRegistryManager manager; + + private final IFile pom; + + private final File pomFile; + + private transient MavenProject mavenProject; + private transient MavenExecutionPlan executionPlan; + + private transient Map<String, Object> sessionProperties; + + private boolean hasValidConfiguration = false; + + // XXX make final, there should be no need to change it + private ResolverConfiguration resolverConfiguration; + + private final long[] timestamp = new long[ProjectRegistryManager.METADATA_PATH.size() + 1]; + + // cached values from mavenProject + private ArtifactKey artifactKey; + private List<String> modules; + private String packaging; + private IPath[] resourceLocations; + private IPath[] testResourceLocations; + private IPath[] compileSourceLocations; + private IPath[] testCompileSourceLocations; + private IPath outputLocation; + private IPath testOutputLocation; + private Set<ArtifactRef> artifacts; + private Set<ArtifactRepositoryRef> artifactRepositories; + private Set<ArtifactRepositoryRef> pluginArtifactRepositories; + + private transient ILifecycleMapping lifecycleMapping; + + public MavenProjectFacade(ProjectRegistryManager manager, IFile pom, MavenProject mavenProject, + ResolverConfiguration resolverConfiguration, ILifecycleMapping lifecycleMapping) { + this.manager = manager; + this.pom = pom; + IPath location = pom.getLocation(); + this.pomFile = location == null ? null : location.toFile(); // save pom file + this.resolverConfiguration = resolverConfiguration; + setMavenProject(mavenProject); + updateTimestamp(); + } + + private void setMavenProject(MavenProject mavenProject) { + this.mavenProject = mavenProject; + + this.artifactKey = new ArtifactKey(mavenProject.getArtifact()); + this.packaging = mavenProject.getPackaging(); + this.modules = mavenProject.getModules(); + + this.resourceLocations = MavenProjectUtils.getResourceLocations(getProject(), mavenProject.getResources()); + this.testResourceLocations = MavenProjectUtils.getResourceLocations(getProject(), mavenProject.getTestResources()); + this.compileSourceLocations = MavenProjectUtils.getSourceLocations(getProject(), mavenProject.getCompileSourceRoots()); + this.testCompileSourceLocations = MavenProjectUtils.getSourceLocations(getProject(),mavenProject.getTestCompileSourceRoots()); + + IPath fullPath = getProject().getFullPath(); + + IPath path = getProjectRelativePath(mavenProject.getBuild().getOutputDirectory()); + this.outputLocation = (path != null) ? fullPath.append(path) : null; + + path = getProjectRelativePath(mavenProject.getBuild().getTestOutputDirectory()); + this.testOutputLocation = path != null ? fullPath.append(path) : null; + + setMavenProjectArtifacts(); + + this.artifactRepositories = new LinkedHashSet<ArtifactRepositoryRef>(); + for(ArtifactRepository repository : mavenProject.getRemoteArtifactRepositories()) { + this.artifactRepositories.add(new ArtifactRepositoryRef(repository)); + } + + this.pluginArtifactRepositories = new LinkedHashSet<ArtifactRepositoryRef>(); + for(ArtifactRepository repository : mavenProject.getPluginArtifactRepositories()) { + this.pluginArtifactRepositories.add(new ArtifactRepositoryRef(repository)); + } + } + + public void setMavenProject() { + setMavenProject(mavenProject); + } + + /** + * Returns project relative paths of resource directories + */ + public IPath[] getResourceLocations() { + return resourceLocations; + } + + /** + * Returns project relative paths of test resource directories + */ + public IPath[] getTestResourceLocations() { + return testResourceLocations; + } + + public IPath[] getCompileSourceLocations() { + return compileSourceLocations; + } + + public IPath[] getTestCompileSourceLocations() { + return testCompileSourceLocations; + } + + /** + * Returns project resource for given file system location or null the location is outside of project. + * + * @param resourceLocation absolute file system location + * @return IPath the full, absolute workspace path resourceLocation + */ + public IPath getProjectRelativePath(String resourceLocation) { + return MavenProjectUtils.getProjectRelativePath(getProject(), resourceLocation); + } + + /** + * Returns the full, absolute path of this project maven build output directory relative to the workspace or null if + * maven build output directory cannot be determined or outside of the workspace. + */ + public IPath getOutputLocation() { + return outputLocation; + } + + /** + * Returns the full, absolute path of this project maven build test output directory relative to the workspace or null + * if maven build output directory cannot be determined or outside of the workspace. + */ + public IPath getTestOutputLocation() { + return testOutputLocation; + } + + public IPath getFullPath() { + return getProject().getFullPath(); + } + + /** + * Lazy load and cache MavenProject instance + */ + public synchronized MavenProject getMavenProject(IProgressMonitor monitor) throws CoreException { + if (mavenProject == null) { + //this used to just pass in 'true' for 'offline'. when the local repo was removed or + //corrupted, though, the project wouldn't load correctly + IMavenConfiguration mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + boolean isOffline = mavenConfiguration.isOffline(); + MavenExecutionResult result = manager.readProjectWithDependencies(pom, resolverConfiguration, // + new MavenUpdateRequest(isOffline, false /* updateSnapshots */), monitor); + mavenProject = result.getProject(); + if (mavenProject == null) { + MultiStatus status = new MultiStatus(IMavenConstants.PLUGIN_ID, 0, Messages.MavenProjectFacade_error, null); + List<Throwable> exceptions = result.getExceptions(); + for (Throwable e : exceptions) { + status.add(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, 0, e.getMessage(), e)); + } + throw new CoreException(status); + } + } + return mavenProject; + } + + public synchronized MavenExecutionPlan getExecutionPlan(IProgressMonitor monitor) throws CoreException { + if (executionPlan == null) { + executionPlan = manager.calculateExecutionPlan(this, monitor); + } + return executionPlan; + } + + public String getPackaging() { + return packaging; + } + + public IProject getProject() { + return pom.getProject(); + } + + public IFile getPom() { + return pom; + } + + public File getPomFile() { + return pomFile; + } + + /** + * Returns the full, absolute path of the given file relative to the workspace. Returns null if the file does not + * exist or is not a member of this project. + */ + public IPath getFullPath(File file) { + return MavenProjectUtils.getFullPath(getProject(), file); + } + + /** + * Visits trough Maven project artifacts and modules + * + * @param visitor a project visitor used to visit Maven project + * @param flags flags to specify visiting behavior. See {@link IMavenProjectVisitor#LOAD}, + * {@link IMavenProjectVisitor#NESTED_MODULES}. + */ + public void accept(IMavenProjectVisitor visitor, int flags) throws CoreException { + acceptImpl(visitor, flags, null); + } + + public void accept(IMavenProjectVisitor2 visitor, int flags, IProgressMonitor monitor) throws CoreException { + acceptImpl(visitor, flags, monitor); + } + + private void acceptImpl(IMavenProjectVisitor visitor, int flags, IProgressMonitor monitor) throws CoreException { + if(visitor.visit(this)) { + if (visitor instanceof IMavenProjectVisitor2 && monitor != null) { + MavenProject mavenProject = ((flags & IMavenProjectVisitor.LOAD) > 0)? getMavenProject(monitor): getMavenProject(); + if (mavenProject != null) { + for(Artifact artifact : mavenProject.getArtifacts()) { + ((IMavenProjectVisitor2) visitor).visit(this, artifact); + } + } + } + } + } + + public List<String> getMavenProjectModules() { + return modules; + } + + public Set<ArtifactRef> getMavenProjectArtifacts() { + return artifacts; + } + + void setMavenProjectArtifacts() { + this.artifacts = ArtifactRef.fromArtifact(mavenProject.getArtifacts()); + } + + public ResolverConfiguration getResolverConfiguration() { + return resolverConfiguration; + } + + public void setResolverConfiguration(ResolverConfiguration configuration) { + resolverConfiguration = configuration; + } + + /** + * @return true if maven project needs to be re-read from disk + */ + public boolean isStale() { + IProject project = getProject(); + int i = 0; + for(IPath path : ProjectRegistryManager.METADATA_PATH) { + if (timestamp[i] != getModificationStamp(project.getFile(path))) { + return true; + } + i++; + } + return timestamp[timestamp.length - 1] != getModificationStamp(pom); + } + + private void updateTimestamp() { + IProject project = getProject(); + int i = 0; + for(IPath path : ProjectRegistryManager.METADATA_PATH) { + timestamp[i] = getModificationStamp(project.getFile(path)); + i++; + } + timestamp[timestamp.length - 1] = getModificationStamp(pom); + } + + private static long getModificationStamp(IFile file) { + /* + * this implementation misses update in the following scenario + * + * 1. two files, A and B, with different content were created with same localTimeStamp + * 2. original A was deleted and B moved to A + * + * See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=160728 + */ + return file.getLocalTimeStamp() + file.getModificationStamp(); + } + + public ArtifactKey getArtifactKey() { + return artifactKey; + } + + public MavenProject getMavenProject() { + return mavenProject; + } + + public synchronized void setSessionProperty(String key, Object value) { + if (sessionProperties == null) { + sessionProperties = new HashMap<String, Object>(); + } + if (value != null) { + sessionProperties.put(key, value); + } else { + sessionProperties.remove(key); + } + } + + public synchronized Object getSessionProperty(String key) { + return sessionProperties != null? sessionProperties.get(key): null; + } + + public Set<ArtifactRepositoryRef> getArtifactRepositoryRefs() { + return artifactRepositories; + } + + public Set<ArtifactRepositoryRef> getPluginArtifactRepositoryRefs() { + return pluginArtifactRepositories; + } + + public ILifecycleMapping getLifecycleMapping(IProgressMonitor monitor) throws CoreException { + if ( lifecycleMapping == null ) { + lifecycleMapping = manager.getLifecycleMapping(pom, getMavenProject(monitor), getResolverConfiguration(), monitor); + } + return lifecycleMapping; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.core.project.IMavenProjectFacade#hasValidConfiguration() + */ + public boolean hasValidConfiguration() { + return hasValidConfiguration; + } + + public void setHasValidConfiguration(boolean hasValidConfiguration) { + this.hasValidConfiguration = hasValidConfiguration; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenRequiredCapability.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenRequiredCapability.java new file mode 100644 index 00000000..1f0c3dbe --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MavenRequiredCapability.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +public class MavenRequiredCapability extends RequiredCapability { + + private static final long serialVersionUID = 3254716937353332553L; + + private final String versionRange; + + private final String scope; + + private final boolean optional; + + private MavenRequiredCapability(String namespace, String id, String versionRange, String scope, boolean optional) { + super(namespace, id); + + if(versionRange == null) { + throw new NullPointerException(); + } + + this.versionRange = versionRange; + this.scope = scope; + this.optional = optional; + } + + public static MavenRequiredCapability createMaven(ArtifactKey key, String scope, boolean optional) { + return new MavenRequiredCapability(MavenCapability.NS_MAVEN_ARTIFACT, MavenCapability.getId(key), key.getVersion(), + scope, optional); + } + + public static MavenRequiredCapability createMavenParent(ArtifactKey key) { + return new MavenRequiredCapability(MavenCapability.NS_MAVEN_PARENT, MavenCapability.getId(key), key.getVersion(), + null, false); + } + + public boolean isPotentialMatch(Capability capability) { + if(capability instanceof MavenCapability && getVersionlessKey().equals(capability.getVersionlessKey())) { + try { + // TODO may need to cache parsed version and versionRange for performance reasons + ArtifactVersion version = new DefaultArtifactVersion(((MavenCapability) capability).getVersion()); + VersionRange range = VersionRange.createFromVersionSpec(versionRange); + return range.containsVersion(version); + } catch(InvalidVersionSpecificationException ex) { + return true; // better safe than sorry + } + } + return false; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getVersionlessKey().toString()); + if(scope != null) { + sb.append(':').append(scope); + } + sb.append('/').append(versionRange); + if(optional) { + sb.append("(optional)"); //$NON-NLS-1$ + } + return sb.toString(); + } + + public int hashCode() { + int hash = getVersionlessKey().hashCode(); + hash = hash * 17 + versionRange.hashCode(); + hash = hash * 17 + (scope != null ? scope.hashCode() : 0); + hash = hash * 17 + (optional ? 1 : 0); + return hash; + } + + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(!(obj instanceof MavenRequiredCapability)) { + return false; + } + MavenRequiredCapability other = (MavenRequiredCapability) obj; + return getVersionlessKey().equals(other.getVersionlessKey()) && versionRange.equals(other.versionRange) + && eq(scope, other.scope) && optional == other.optional; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MutableProjectRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MutableProjectRegistry.java new file mode 100644 index 00000000..a2526c98 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/MutableProjectRegistry.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * WorkspaceStateDelta + * + * @author igor + */ +public class MutableProjectRegistry extends BasicProjectRegistry implements IProjectRegistry { + + private static final long serialVersionUID = -4106047696261024741L; + + private final ProjectRegistry parent; + + private final int parentVersion; + + private boolean closed; + + public MutableProjectRegistry(ProjectRegistry state) { + super(state); + this.parent = state; + this.parentVersion = state.getVersion(); + } + + private void assertNotClosed() { + if(closed) { + throw new IllegalStateException("Can't modify closed MutableProjectRegistry"); //$NON-NLS-1$ + } + } + + public void setProject(IFile pom, MavenProjectFacade facade) { + assertNotClosed(); + + // remove + MavenProjectFacade oldFacade = workspacePoms.remove(pom); + if(oldFacade != null) { + workspaceArtifacts.remove(oldFacade.getArtifactKey()); + } + if(facade != null) { + // Add the project to workspaceProjects map + workspacePoms.put(pom, facade); + + // Add the project to workspaceArtifacts map + workspaceArtifacts.put(facade.getArtifactKey(), pom); + } + } + + public void removeProject(IFile pom, ArtifactKey mavenProject) { + assertNotClosed(); + + // remove project from requiredCapabilities map + removeRequiredCapabilities(pom); + + // Remove the project from workspaceProjects, projectRequirements and projectCapabilities maps + workspacePoms.remove(pom); + projectRequirements.remove(pom); + projectCapabilities.remove(pom); + + // Remove the project from workspaceArtifacts map + if(mavenProject != null) { + workspaceArtifacts.remove(mavenProject); + } + } + + static boolean isSameProject(IResource r1, IResource r2) { + if(r1 == null || r2 == null) { + return false; + } + return r1.getProject().equals(r2.getProject()); + } + + public Set<IFile> removeWorkspaceModules(IFile pom, ArtifactKey mavenProject) { + assertNotClosed(); + + return getDependents(MavenCapability.createMavenParent(mavenProject), true); + } + + public boolean isStale() { + return parentVersion != parent.getVersion(); + } + + public void close() { + this.closed = true; + + clear(); + } + + private boolean isClosed() { + return closed; + } + + // IProjectRegistry + + public MavenProjectFacade getProjectFacade(IFile pom) { + if(isClosed()) { + return parent.getProjectFacade(pom); + } + return super.getProjectFacade(pom); + } + + public MavenProjectFacade getProjectFacade(String groupId, String artifactId, String version) { + if(isClosed()) { + return parent.getProjectFacade(groupId, artifactId, version); + } + return super.getProjectFacade(groupId, artifactId, version); + } + + public MavenProjectFacade[] getProjects() { + if(isClosed()) { + return parent.getProjects(); + } + return super.getProjects(); + } + + public IFile getWorkspaceArtifact(ArtifactKey key) { + if(isClosed()) { + return parent.getWorkspaceArtifact(key); + } + return super.getWorkspaceArtifact(key); + } + + // low level access and manipulation + + /** + * Returns all workspace projects that require given Capability. + */ + public Set<IFile> getDependents(Capability capability, boolean remove) { + Map<RequiredCapability, Set<IFile>> rs = requiredCapabilities.get(capability.getVersionlessKey()); + if(rs == null) { + return Collections.emptySet(); + } + Set<IFile> result = new LinkedHashSet<IFile>(); + Iterator<Entry<RequiredCapability, Set<IFile>>> iter = rs.entrySet().iterator(); + while(iter.hasNext()) { + Entry<RequiredCapability, Set<IFile>> entry = iter.next(); + if(entry.getKey().isPotentialMatch(capability)) { + result.addAll(entry.getValue()); + if(remove) { + iter.remove(); + } + } + } + if(remove && rs.isEmpty()) { + requiredCapabilities.remove(capability.getVersionlessKey()); + } + return result; + } + + /** + * Returns all workspace projects that require given versionless Capability. + */ + public Set<IFile> getDependents(VersionlessKey capability, boolean remove) { + Map<RequiredCapability, Set<IFile>> rs; + if(remove) { + rs = requiredCapabilities.remove(capability); + } else { + rs = requiredCapabilities.get(capability); + } + if(rs == null) { + return Collections.emptySet(); + } + Set<IFile> result = new LinkedHashSet<IFile>(); + for(Set<IFile> dependents : rs.values()) { + result.addAll(dependents); + } + return result; + } + + private void addRequiredCapability(IFile pom, RequiredCapability req) { + Map<RequiredCapability, Set<IFile>> keyEntry = requiredCapabilities.get(req.getVersionlessKey()); + if(keyEntry == null) { + keyEntry = new HashMap<RequiredCapability, Set<IFile>>(); + requiredCapabilities.put(req.getVersionlessKey(), keyEntry); + } + Set<IFile> poms = keyEntry.get(req); + if(poms == null) { + poms = new HashSet<IFile>(); + keyEntry.put(req, poms); + } + poms.add(pom); + } + + public Set<Capability> setCapabilities(IFile pom, Set<Capability> capabilities) { + return capabilities != null ? projectCapabilities.put(pom, capabilities) : projectCapabilities.remove(pom); + } + + public Set<RequiredCapability> setRequirements(IFile pom, Set<RequiredCapability> requirements) { + removeRequiredCapabilities(pom); + if(requirements != null) { + for(RequiredCapability requirement : requirements) { + addRequiredCapability(pom, requirement); + } + return projectRequirements.put(pom, requirements); + } + return projectRequirements.remove(pom); + } + + private void removeRequiredCapabilities(IFile pom) { + // TODO likely too slow + Iterator<Entry<VersionlessKey, Map<RequiredCapability, Set<IFile>>>> keysIter = requiredCapabilities.entrySet() + .iterator(); + while(keysIter.hasNext()) { + Entry<VersionlessKey, Map<RequiredCapability, Set<IFile>>> keysEntry = keysIter.next(); + Iterator<Entry<RequiredCapability, Set<IFile>>> requirementsIter = keysEntry.getValue().entrySet().iterator(); + while(requirementsIter.hasNext()) { + Entry<RequiredCapability, Set<IFile>> requirementsEntry = requirementsIter.next(); + requirementsEntry.getValue().remove(pom); + if(requirementsEntry.getValue().isEmpty()) { + // was last project that required this capability + requirementsIter.remove(); + } + } + if(keysEntry.getValue().isEmpty()) { + // was last project that required this capability versionless key + keysIter.remove(); + } + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistry.java new file mode 100644 index 00000000..0ea78e23 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistry.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; + + +/** + * Registry of all Maven workspace projects and their inter-dependencies. Dependencies are modelled as generic + * requirement/capability match and can represent any dependencies, not just Maven. The only way to change registry + * contents is via {@link #apply(MutableProjectRegistry)} call. This class is thread safe. + * + * @author Igor Fedorenko + */ +public class ProjectRegistry extends BasicProjectRegistry implements Serializable, IProjectRegistry { + + private static final long serialVersionUID = -5813062417528479711L; + + private transient int version; + + public synchronized MavenProjectFacade getProjectFacade(IFile pom) { + return super.getProjectFacade(pom); + } + + public synchronized MavenProjectFacade getProjectFacade(String groupId, String artifactId, String version) { + return super.getProjectFacade(groupId, artifactId, version); + } + + public synchronized MavenProjectFacade[] getProjects() { + return super.getProjects(); + } + + public synchronized IFile getWorkspaceArtifact(ArtifactKey key) { + return super.getWorkspaceArtifact(key); + } + + public synchronized List<MavenProjectChangedEvent> apply(MutableProjectRegistry newState) + throws StaleMutableProjectRegistryException { + if(newState.isStale()) { + throw new StaleMutableProjectRegistryException(); + } + + ArrayList<MavenProjectChangedEvent> events = new ArrayList<MavenProjectChangedEvent>(); + + // removed projects + for(MavenProjectFacade facade : workspacePoms.values()) { + if(!newState.workspacePoms.containsKey(facade.getPom())) { + MavenProjectChangedEvent event = new MavenProjectChangedEvent( // + facade.getPom(), // + MavenProjectChangedEvent.KIND_REMOVED, // + MavenProjectChangedEvent.FLAG_NONE, // + facade /*old*/, // + null /*new*/); + events.add(event); + } + } + + // changed and new projects + for(MavenProjectFacade facade : newState.workspacePoms.values()) { + MavenProjectFacade old = workspacePoms.get(facade.getPom()); + if(facade != old) { // not the same instance! + MavenProjectChangedEvent event; + if(old != null) { + int flags = hasDependencyChange(old.getPom(), newState) ? MavenProjectChangedEvent.FLAG_DEPENDENCIES + : MavenProjectChangedEvent.FLAG_NONE; + event = new MavenProjectChangedEvent(facade.getPom(), // + MavenProjectChangedEvent.KIND_CHANGED, // + flags, // + old /*old*/, // + facade /*new*/); + + } else { + event = new MavenProjectChangedEvent(facade.getPom(), // + MavenProjectChangedEvent.KIND_ADDED, // + MavenProjectChangedEvent.FLAG_NONE, // + null /*old*/, // + facade /*new*/); + } + events.add(event); + } + } + + replaceWith(newState); + + version++ ; + + return events; + } + + public synchronized int getVersion() { + return version; + } + + private boolean hasDependencyChange(IFile pom, MutableProjectRegistry newState) { + Set<RequiredCapability> oldRequirements = getProjectRequirements(pom); + Set<RequiredCapability> requirements = newState.getProjectRequirements(pom); + + return ProjectRegistryManager.hasDiff(oldRequirements, requirements); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java new file mode 100644 index 00000000..d1d708b1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryManager.java @@ -0,0 +1,854 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.osgi.service.prefs.BackingStoreException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.DefaultMavenExecutionResult; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.artifact.MavenMetadataCache; +import org.apache.maven.properties.internal.EnvironmentUtils; +import org.apache.maven.repository.DelegatingLocalArtifactRepository; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.ExtensionReader; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.embedder.MavenImpl; +import org.eclipse.m2e.core.internal.lifecycle.LifecycleMappingFactory; +import org.eclipse.m2e.core.internal.project.DependencyResolutionContext; +import org.eclipse.m2e.core.internal.project.IManagedCache; +import org.eclipse.m2e.core.internal.project.MissingLifecycleMapping; +import org.eclipse.m2e.core.internal.project.MojoExecutionProjectConfigurator; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenUpdateRequest; +import org.eclipse.m2e.core.project.ResolverConfiguration; +import org.eclipse.m2e.core.project.configurator.CustomizableLifecycleMapping; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.project.configurator.NoopLifecycleMapping; + +/** + * This class keeps track of all maven projects present in the workspace and + * provides mapping between Maven and the workspace. + */ +public class ProjectRegistryManager { + + public static boolean DEBUG = MavenPlugin.getDefault().isDebugging() + & Boolean.parseBoolean(Platform.getDebugOption(IMavenConstants.PLUGIN_ID + "/debug/projectManager")); //$NON-NLS-1$ + + static final String ARTIFACT_TYPE_POM = "pom"; //$NON-NLS-1$ + static final String ARTIFACT_TYPE_JAR = "jar"; //$NON-NLS-1$ + public static final String ARTIFACT_TYPE_JAVA_SOURCE = "java-source"; //$NON-NLS-1$ + public static final String ARTIFACT_TYPE_JAVADOC = "javadoc"; //$NON-NLS-1$ + + private static final String P_VERSION = "version"; //$NON-NLS-1$ + private static final String P_RESOLVE_WORKSPACE_PROJECTS = "resolveWorkspaceProjects"; //$NON-NLS-1$ + private static final String P_ACTIVE_PROFILES = "activeProfiles"; //$NON-NLS-1$ + + private static final String VERSION = "1"; //$NON-NLS-1$ + + /** + * Path of project metadata files, relative to the project. These + * files are used to determine if project dependencies need to be + * updated. + * + * Note that path of pom.xml varies for nested projects and pom.xml + * are treated separately. + */ + public static final List<? extends IPath> METADATA_PATH = Arrays.asList( // + new Path(".project"), // //$NON-NLS-1$ + new Path(".classpath"), // //$NON-NLS-1$ + new Path(".settings/org.eclipse.m2e.prefs")); // dirty trick! //$NON-NLS-1$ + + private final ProjectRegistry projectRegistry; + + private final MavenConsole console; + private final MavenImpl maven; + + private final IMavenMarkerManager markerManager; + + private final ProjectRegistryReader stateReader; + + private final Set<IMavenProjectChangedListener> projectChangeListeners = new LinkedHashSet<IMavenProjectChangedListener>(); + + private volatile Thread syncRefreshThread; + + public ProjectRegistryManager(MavenImpl maven, MavenConsole console, File stateLocationDir, boolean readState, + IMavenMarkerManager mavenMarkerManager) { + this.console = console; + this.markerManager = mavenMarkerManager; + this.maven = maven; + + this.stateReader = new ProjectRegistryReader(stateLocationDir); + + ProjectRegistry state = readState && stateReader != null ? stateReader.readWorkspaceState(this) : null; + this.projectRegistry = (state != null && state.isValid()) ? state : new ProjectRegistry(); + } + + /** + * Creates or returns cached MavenProjectFacade for the given project. + * + * This method will not block if called from IMavenProjectChangedListener#mavenProjectChanged + */ + public MavenProjectFacade create(IProject project, IProgressMonitor monitor) { + return create(getPom(project), false, monitor); + } + + /** + * Returns MavenProjectFacade corresponding to the pom. + * + * This method first looks in the project cache, then attempts to load + * the pom if the pom is not found in the cache. In the latter case, + * workspace resolution is assumed to be enabled for the pom but the pom + * will not be added to the cache. + */ + public MavenProjectFacade create(IFile pom, boolean load, IProgressMonitor monitor) { + if(pom == null) { + return null; + } + + // MavenProjectFacade projectFacade = (MavenProjectFacade) workspacePoms.get(pom.getFullPath()); + MavenProjectFacade projectFacade = projectRegistry.getProjectFacade(pom); + if(projectFacade == null && load) { + ResolverConfiguration configuration = readResolverConfiguration(pom.getProject()); + //this used to just pass in 'true' for 'offline'. when the local repo was removed or + //corrupted, though, the project wouldn't load correctly + IMavenConfiguration mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + boolean isOffline = mavenConfiguration.isOffline(); + MavenExecutionResult executionResult = readProjectWithDependencies(projectRegistry, pom, configuration, // + new MavenUpdateRequest(isOffline, false /* updateSnapshots */), + monitor); + MavenProject mavenProject = executionResult.getProject(); + if(mavenProject != null) { + projectFacade = new MavenProjectFacade(this, pom, mavenProject, configuration, null); + } else { + List<Throwable> exceptions = executionResult.getExceptions(); + if (exceptions != null) { + for(Throwable ex : exceptions) { + String msg = "Failed to read Maven project"; + console.logError(msg); + console.logError(ex.toString()); + MavenLogger.log(msg, ex); + } + } + } + } + return projectFacade; + } + + public boolean saveResolverConfiguration(IProject project, ResolverConfiguration configuration) { + IScopeContext projectScope = new ProjectScope(project); + IEclipsePreferences projectNode = projectScope.getNode(IMavenConstants.PLUGIN_ID); + if(projectNode != null) { + projectNode.put(P_VERSION, VERSION); + + projectNode.putBoolean(P_RESOLVE_WORKSPACE_PROJECTS, configuration.shouldResolveWorkspaceProjects()); + + projectNode.put(P_ACTIVE_PROFILES, configuration.getActiveProfiles()); + + try { + projectNode.flush(); + return true; + } catch(BackingStoreException ex) { + MavenLogger.log("Failed to save resolver configuration", ex); + } + } + + return false; + } + + public ResolverConfiguration readResolverConfiguration(IProject project) { + IScopeContext projectScope = new ProjectScope(project); + IEclipsePreferences projectNode = projectScope.getNode(IMavenConstants.PLUGIN_ID); + if(projectNode==null) { + return new ResolverConfiguration(); + } + + String version = projectNode.get(P_VERSION, null); + if(version == null) { // migrate from old config + // return LegacyBuildPathManager.getResolverConfiguration(project); + return new ResolverConfiguration(); + } + + ResolverConfiguration configuration = new ResolverConfiguration(); + configuration.setResolveWorkspaceProjects(projectNode.getBoolean(P_RESOLVE_WORKSPACE_PROJECTS, false)); + + configuration.setActiveProfiles(projectNode.get(P_ACTIVE_PROFILES, "")); //$NON-NLS-1$ + return configuration; + } + + IFile getPom(IProject project) { + if (project == null || !project.isAccessible()) { + // XXX sensible handling + return null; + } + return project.getFile(IMavenConstants.POM_FILE_NAME); + } + + /** + * Removes specified poms from the cache. + * Adds dependent poms to pomSet but does not directly refresh dependent poms. + * Recursively removes all nested modules if appropriate. + * + * @return a {@link Set} of {@link IFile} affected poms + */ + public Set<IFile> remove(MutableProjectRegistry state, Set<IFile> poms, boolean force) { + Set<IFile> pomSet = new LinkedHashSet<IFile>(); + for (Iterator<IFile> it = poms.iterator(); it.hasNext(); ) { + IFile pom = it.next(); + MavenProjectFacade facade = state.getProjectFacade(pom); + if (force || facade == null || facade.isStale()) { + pomSet.addAll(remove(state, pom)); + } + } + return pomSet; + } + + /** + * Removes the pom from the cache. + * Adds dependent poms to pomSet but does not directly refresh dependent poms. + * Recursively removes all nested modules if appropriate. + * + * @return a {@link Set} of {@link IFile} affected poms + */ + public Set<IFile> remove(MutableProjectRegistry state, IFile pom) { + MavenProjectFacade facade = state.getProjectFacade(pom); + ArtifactKey mavenProject = facade != null ? facade.getArtifactKey() : null; + + flushCaches(pom, facade); + + if (mavenProject == null) { + state.removeProject(pom, null); + return Collections.emptySet(); + } + + Set<IFile> pomSet = new LinkedHashSet<IFile>(); + + pomSet.addAll(state.getDependents(MavenCapability.createMaven(mavenProject), false)); + pomSet.addAll(state.getDependents(MavenCapability.createMavenParent(mavenProject), false)); // TODO check packaging + state.removeProject(pom, mavenProject); + + pomSet.addAll(refreshWorkspaceModules(state, pom, mavenProject)); + + pomSet.remove(pom); + + return pomSet; + } + + private void flushCaches(IFile pom, MavenProjectFacade facade) { + ArtifactKey key = null; + MavenProject project = null; + + if (facade != null) { + key = facade.getArtifactKey(); + project = facade.getMavenProject(); + } + try { + IManagedCache cache = (IManagedCache) maven.getPlexusContainer().lookup(MavenMetadataCache.class); + cache.removeProject(pom, key); + } catch(ComponentLookupException ex) { + // can't really happen + } catch(CoreException ex) { + // can't really happen + } + if (project != null) { + getMaven().xxxRemoveExtensionsRealm(project); + } + } + + /** + * This method acquires workspace root's lock and sends project change events. + * It is meant for synchronous registry updates. + */ + public void refresh(MavenUpdateRequest request, IProgressMonitor monitor) throws CoreException { + SubMonitor progress = SubMonitor.convert(monitor, Messages.ProjectRegistryManager_task_refreshing, 100); + ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRoot(); + Job.getJobManager().beginRule(rule, progress); + try { + syncRefreshThread = Thread.currentThread(); + + MutableProjectRegistry newState = newMutableProjectRegistry(); + try { + refresh(newState, request, progress.newChild(95)); + + applyMutableProjectRegistry(newState, progress.newChild(5)); + } finally { + newState.close(); + } + } finally { + syncRefreshThread = null; + Job.getJobManager().endRule(rule); + } + } + + void refresh(MutableProjectRegistry newState, MavenUpdateRequest updateRequest, IProgressMonitor monitor) throws CoreException { + MavenExecutionRequest executionRequest = getMaven().createExecutionRequest(monitor); + + DependencyResolutionContext context = new DependencyResolutionContext(updateRequest, executionRequest); + + refresh(newState, context, monitor); + } + + protected void refresh(MutableProjectRegistry newState, DependencyResolutionContext context, IProgressMonitor monitor) + throws CoreException { + while(!context.isEmpty()) { + Map<IFile, MavenProjectFacade> newFacades = new LinkedHashMap<IFile, MavenProjectFacade>(); + + // phase 1: build projects without dependencies and populate workspace with known projects + while(!context.isEmpty()) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + if(newState.isStale() || (syncRefreshThread != null && syncRefreshThread != Thread.currentThread())) { + throw new StaleMutableProjectRegistryException(); + } + + IFile pom = context.pop(); + + monitor.subTask(NLS.bind(Messages.ProjectRegistryManager_task_project, pom.getProject().getName())); + MavenProjectFacade newFacade = null; + if(pom.isAccessible() && pom.getProject().hasNature(IMavenConstants.NATURE_ID)) { + MavenProjectFacade oldFacade = newState.getProjectFacade(pom); + + if(!context.isForce(pom) && oldFacade != null && !oldFacade.isStale()) { + // skip refresh if not forced and up-to-date facade + continue; + } + + flushCaches(pom, oldFacade); + + newFacade = readMavenProject(pom, context, newState, monitor); + } + + newState.setProject(pom, newFacade); + + // at this point project facade and project capabilities/requirements are inconsistent in the state + // this will be reconciled during the second phase + + newFacades.put(pom, newFacade); // stash work for the second phase + } + + // TODO theoretically, there is no need to re-read projects during second resolution phase + // only re-resolve dependencies. + + // phase 2: resolve project dependencies + for(Map.Entry<IFile, MavenProjectFacade> entry : newFacades.entrySet()) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + if(newState.isStale() || (syncRefreshThread != null && syncRefreshThread != Thread.currentThread())) { + throw new StaleMutableProjectRegistryException(); + } + + IFile pom = entry.getKey(); + MavenProjectFacade newFacade = entry.getValue(); + + Set<Capability> capabilities = null; + Set<RequiredCapability> requirements = null; + if(newFacade != null) { + monitor.subTask(NLS.bind(Messages.ProjectRegistryManager_task_project,newFacade.getProject().getName())); + + // TODO is this expansive? + MavenExecutionRequest mavenRequest = getConfiguredExecutionRequest(context, newState, newFacade.getPom(), + newFacade.getResolverConfiguration()); + mavenRequest.getProjectBuildingRequest().setProject(newFacade.getMavenProject()); + mavenRequest.getProjectBuildingRequest().setResolveDependencies(true); + + capabilities = new LinkedHashSet<Capability>(); + requirements = new LinkedHashSet<RequiredCapability>(); + + Capability mavenParentCapability = MavenCapability.createMavenParent(newFacade.getArtifactKey()); + + // maven projects always have these capabilities + capabilities.add(MavenCapability.createMaven(newFacade.getArtifactKey())); + capabilities.add(mavenParentCapability); // TODO consider packaging + + AbstractMavenDependencyResolver resolver = getMavenDependencyResolver(newFacade, monitor); + resolver.setContextProjectRegistry(newState); + try { + resolver.resolveProjectDependencies(newFacade, mavenRequest, capabilities, requirements, monitor); + } finally { + resolver.setContextProjectRegistry(null); + } + + newFacade.setMavenProjectArtifacts(); + + // always refresh child modules + context.forcePomFiles(newState.getDependents(mavenParentCapability, true)); + } else { + if(pom.isAccessible() && pom.getProject().hasNature(IMavenConstants.NATURE_ID)) { + try { + // MNGECLIPSE-605 embedder is not able to resolve the project due to missing configuration in the parent + Model model = getMaven().readModel(pom.getLocation().toFile()); + if(model != null && model.getParent() != null) { + Parent parent = model.getParent(); + if(parent.getGroupId() != null && parent.getArtifactId() != null && parent.getVersion() != null) { + ArtifactKey parentKey = new ArtifactKey(parent.getGroupId(), parent.getArtifactId(), + parent.getVersion(), null); + requirements = new HashSet<RequiredCapability>(); + requirements.add(MavenRequiredCapability.createMavenParent(parentKey)); + } + } + } catch(Exception ex) { + // we've tried our best, there is nothing else we can do + } + } + } + + Set<Capability> oldCapabilities = newState.setCapabilities(pom, capabilities); + // if our capabilities changed, recalculate everyone who depends on new/changed/removed capabilities + Set<Capability> changedCapabilities = diff(oldCapabilities, capabilities); + for(Capability capability : changedCapabilities) { + context.forcePomFiles(newState.getDependents(capability, true)); + } + + Set<RequiredCapability> oldRequirements = newState.setRequirements(pom, requirements); + // if our dependencies changed, recalculate everyone who depends on us + // this is needed to deal with transitive dependency resolution in maven + if(oldCapabilities != null && hasDiff(oldRequirements, requirements)) { + for(Capability capability : oldCapabilities) { + context.forcePomFiles(newState.getDependents(capability.getVersionlessKey(), true)); + } + } + + monitor.worked(1); + } + } + } + + static <T> Set<T> diff(Set<T> a, Set<T> b) { + if(a == null || a.isEmpty()) { + if(b == null || b.isEmpty()) { + return Collections.emptySet(); + } + return b; + } + if(b == null || b.isEmpty()) { + return a; + } + Set<T> result = new HashSet<T>(); + Set<T> t; + + t = new HashSet<T>(a); t.removeAll(b); result.addAll(t); + t = new HashSet<T>(b); t.removeAll(a); result.addAll(t); + + return result; + } + + static <T> boolean hasDiff(Set<T> a, Set<T> b) { + if(a == null || a.isEmpty()) { + return b != null && !b.isEmpty(); + } + + if(b == null || b.isEmpty()) { + return true; + } + + if(a.size() != b.size()) { + return true; + } + + Iterator<T> oldIter = a.iterator(); + Iterator<T> iter = b.iterator(); + + while(oldIter.hasNext()) { + T oldRequirement = oldIter.next(); + T requirement = iter.next(); + if (!oldRequirement.equals(requirement)) { + return true; + } + } + return false; + } + + private AbstractMavenDependencyResolver getMavenDependencyResolver(MavenProjectFacade newFacade, IProgressMonitor monitor) throws CoreException { + ILifecycleMapping lifecycleMapping = newFacade.getLifecycleMapping(monitor); + + if (lifecycleMapping instanceof ILifecycleMapping2) { + AbstractMavenDependencyResolver resolver = ((ILifecycleMapping2) lifecycleMapping).getDependencyResolver(monitor); + resolver.setManager(this); + return resolver; + } + + return new DefaultMavenDependencyResolver(this, markerManager); + } + + protected MavenExecutionRequest getConfiguredExecutionRequest(DependencyResolutionContext context, + MutableProjectRegistry state, IFile pom, ResolverConfiguration resolverConfiguration) throws CoreException { + MavenExecutionRequest mavenRequest = DefaultMavenExecutionRequest.copy(context.getExecutionRequest()); + configureExecutionRequest(mavenRequest, state, pom, resolverConfiguration); + getMaven().populateDefaults(mavenRequest); + mavenRequest.setOffline(context.getRequest().isOffline()); + return mavenRequest; + } + + private MavenProjectFacade readMavenProject(IFile pom, DependencyResolutionContext context, + MutableProjectRegistry state, IProgressMonitor monitor) throws CoreException { + markerManager.deleteMarkers(pom, IMavenConstants.MARKER_POM_LOADING_ID); + + ResolverConfiguration resolverConfiguration = readResolverConfiguration(pom.getProject()); + + MavenProject mavenProject = null; + MavenExecutionResult mavenResult = null; + if (pom.isAccessible()) { + MavenExecutionRequest mavenRequest = getConfiguredExecutionRequest(context, state, pom, resolverConfiguration); + mavenResult = getMaven().readProject(mavenRequest, monitor); + mavenProject = mavenResult.getProject(); + } + + markerManager.addEditorHintMarkers(pom, IMavenConstants.MARKER_POM_LOADING_ID); + if (mavenProject == null) { + markerManager.addMarkers(pom, IMavenConstants.MARKER_POM_LOADING_ID, mavenResult); + return null; + } + + ILifecycleMapping lifecycleMapping = getLifecycleMapping(pom, mavenProject, resolverConfiguration, monitor); + + // don't cache maven session + getMaven().detachFromSession(mavenProject); + + // create and return new project facade + MavenProjectFacade mavenProjectFacade = new MavenProjectFacade(ProjectRegistryManager.this, pom, mavenProject, + resolverConfiguration, lifecycleMapping); + + MavenPlugin.getDefault().getProjectConfigurationManager().validateProjectConfiguration(mavenProjectFacade, monitor); + + return mavenProjectFacade; + } + + MavenExecutionPlan calculateExecutionPlan(MavenProjectFacade facade, IProgressMonitor monitor) throws CoreException { + return calculateExecutionPlan(projectRegistry, facade, monitor); + } + + private MavenExecutionPlan calculateExecutionPlan(IProjectRegistry state, MavenProjectFacade facade, IProgressMonitor monitor) throws CoreException { + MavenExecutionRequest request = createExecutionRequest(state, facade.getPom(), facade.getResolverConfiguration(), monitor); + request.setGoals(Arrays.asList("deploy")); //$NON-NLS-1$ + return getMaven().calculateExecutionPlan(request, facade.getMavenProject(monitor), monitor); + } + + public IFile getModulePom(IFile pom, String moduleName) { + return pom.getParent().getFile(new Path(moduleName).append(IMavenConstants.POM_FILE_NAME)); + } + + private Set<IFile> refreshWorkspaceModules(MutableProjectRegistry state, IFile pom, ArtifactKey mavenProject) { + if (mavenProject == null) { + return Collections.emptySet(); + } + + return state.removeWorkspaceModules(pom, mavenProject); + } + + public void addMavenProjectChangedListener(IMavenProjectChangedListener listener) { + synchronized (projectChangeListeners) { + projectChangeListeners.add(listener); + } + } + + public void removeMavenProjectChangedListener(IMavenProjectChangedListener listener) { + if(listener == null) { + return; + } + synchronized (projectChangeListeners) { + projectChangeListeners.remove(listener); + } + } + + public void notifyProjectChangeListeners(List<MavenProjectChangedEvent> events, IProgressMonitor monitor) { + if(events.size() > 0) { + MavenProjectChangedEvent[] eventsArray = events.toArray(new MavenProjectChangedEvent[events.size()]); + ArrayList<IMavenProjectChangedListener> listeners = new ArrayList<IMavenProjectChangedListener>(); + synchronized(this.projectChangeListeners) { + listeners.addAll(this.projectChangeListeners); + } + listeners.addAll(ExtensionReader.readProjectChangedEventListenerExtentions()); + for(IMavenProjectChangedListener listener : listeners) { + listener.mavenProjectChanged(eventsArray, monitor); + } + } + } + + public MavenProjectFacade getMavenProject(String groupId, String artifactId, String version) { + return projectRegistry.getProjectFacade(groupId, artifactId, version); + } + + MavenExecutionResult readProjectWithDependencies(IFile pomFile, ResolverConfiguration resolverConfiguration, + MavenUpdateRequest updateRequest, IProgressMonitor monitor) { + return readProjectWithDependencies(projectRegistry, pomFile, resolverConfiguration, updateRequest, monitor); + } + + private MavenExecutionResult readProjectWithDependencies(IProjectRegistry state, IFile pomFile, ResolverConfiguration resolverConfiguration, + MavenUpdateRequest updateRequest, IProgressMonitor monitor) { + + try { + MavenExecutionRequest request = createExecutionRequest(state, pomFile, resolverConfiguration, monitor); + getMaven().populateDefaults(request); + request.setOffline(updateRequest.isOffline()); + request.getProjectBuildingRequest().setResolveDependencies(true); + return getMaven().readProject(request, monitor); + } catch(CoreException ex) { + DefaultMavenExecutionResult result = new DefaultMavenExecutionResult(); + result.addException(ex); + return result; + } + + } + + public IMavenProjectFacade[] getProjects() { + return projectRegistry.getProjects(); + } + + public IMavenProjectFacade getProject(IProject project) { + return projectRegistry.getProjectFacade(getPom(project)); + } + + public boolean setResolverConfiguration(IProject project, ResolverConfiguration configuration) { + MavenProjectFacade projectFacade = create(project, new NullProgressMonitor()); + if(projectFacade!=null) { + projectFacade.setResolverConfiguration(configuration); + } + return saveResolverConfiguration(project, configuration); + } + + /** + * Context + */ + static class Context { + final IProjectRegistry state; + + final ResolverConfiguration resolverConfiguration; + + final IFile pom; + + Context(IProjectRegistry state, ResolverConfiguration resolverConfiguration, IFile pom) { + this.state = state; + this.resolverConfiguration = resolverConfiguration; + this.pom = pom; + } + } + + public MavenExecutionRequest createExecutionRequest(IFile pom, ResolverConfiguration resolverConfiguration, IProgressMonitor monitor) throws CoreException { + return createExecutionRequest(projectRegistry, pom, resolverConfiguration, monitor); + } + + private MavenExecutionRequest createExecutionRequest(IProjectRegistry state, IFile pom, ResolverConfiguration resolverConfiguration, IProgressMonitor monitor) throws CoreException { + MavenExecutionRequest request = getMaven().createExecutionRequest(monitor); + + return configureExecutionRequest(request, state, pom, resolverConfiguration); + } + + private MavenExecutionRequest configureExecutionRequest(MavenExecutionRequest request, IProjectRegistry state, + IFile pom, ResolverConfiguration resolverConfiguration) throws CoreException { + request.setPom(pom.getLocation().toFile()); + + request.addActiveProfiles(resolverConfiguration.getActiveProfileList()); + + // temporary solution for https://issues.sonatype.org/browse/MNGECLIPSE-1607 + Properties systemProperties = new Properties(); + EnvironmentUtils.addEnvVars(systemProperties); + systemProperties.putAll(System.getProperties()); + request.setSystemProperties(systemProperties); + + // eclipse workspace repository implements both workspace dependency resolution + // and inter-module dependency resolution for multi-module projects. + + request.setLocalRepository(getMaven().getLocalRepository()); + request.setWorkspaceReader(getWorkspaceReader(state, pom, resolverConfiguration)); + + return request; + } + + private EclipseWorkspaceArtifactRepository getWorkspaceReader(IProjectRegistry state, IFile pom, + ResolverConfiguration resolverConfiguration) { + Context context = new Context(state, resolverConfiguration, pom); + EclipseWorkspaceArtifactRepository workspaceReader = new EclipseWorkspaceArtifactRepository(context); + return workspaceReader; + } + + public MavenArtifactRepository getWorkspaceLocalRepository() throws CoreException { + ResolverConfiguration resolverConfiguration = new ResolverConfiguration(); + resolverConfiguration.setResolveWorkspaceProjects(true); + EclipseWorkspaceArtifactRepository workspaceReader = getWorkspaceReader(projectRegistry, null, + resolverConfiguration); + + DelegatingLocalArtifactRepository localRepo = new DelegatingLocalArtifactRepository(getMaven().getLocalRepository()); + localRepo.setIdeWorkspace(workspaceReader); + + return localRepo; + } + + MutableProjectRegistry newMutableProjectRegistry() { + return new MutableProjectRegistry(projectRegistry); + } + + /** + * Applies mutable project registry to the primary project registry and + * and corresponding MavenProjectChangedEvent's to all registered + * IMavenProjectChangedListener's. + * + * This method must be called from a thread holding workspace root's lock. + * + * @throws StaleMutableProjectRegistryException if primary project registry + * was modified after mutable registry has been created + */ + void applyMutableProjectRegistry(MutableProjectRegistry newState, IProgressMonitor monitor) { + List<MavenProjectChangedEvent> events = projectRegistry.apply(newState); + stateReader.writeWorkspaceState(projectRegistry); + notifyProjectChangeListeners(events, monitor); + } + + IMaven getMaven() { + return maven; + } + + + ILifecycleMapping getLifecycleMapping(IFile pom, MavenProject project, ResolverConfiguration resolverConfiguration, IProgressMonitor monitor) { + String mappingId = null; + + if (project.equals(pom.getParent())) { + throw new IllegalArgumentException("Nested workspace module " + pom); //$NON-NLS-1$ + } + + if ("pom".equals(project.getPackaging())) { //$NON-NLS-1$ + return new NoopLifecycleMapping(); + } + + Plugin plugin = project.getPlugin( "org.eclipse.m2e:lifecycle-mapping" ); //$NON-NLS-1$ + + Xpp3Dom configuration = null; + + if (plugin != null) { + configuration = (Xpp3Dom) plugin.getConfiguration(); + if (configuration != null) { + Xpp3Dom mappingIdDom = configuration.getChild("mappingId"); //$NON-NLS-1$ + if (mappingIdDom != null) { + mappingId = mappingIdDom.getValue(); + } + } + } + + ILifecycleMapping lifecycleMapping = null; + if (mappingId == null || mappingId.length() <= 0) { + lifecycleMapping = LifecycleMappingFactory.getLifecycleMappingFor(project.getPackaging()); + } else { + lifecycleMapping = LifecycleMappingFactory.getLifecycleMapping(mappingId); + } + + if(configuration != null && lifecycleMapping instanceof CustomizableLifecycleMapping) { + CustomizableLifecycleMapping custmizableMapping = (CustomizableLifecycleMapping) lifecycleMapping; + + Xpp3Dom configuratorsDom = configuration.getChild("configurators"); //$NON-NLS-1$ + Xpp3Dom executionsDom = configuration.getChild("mojoExecutions"); //$NON-NLS-1$ + + if(configuratorsDom != null) { + for(Xpp3Dom configuratorDom : configuratorsDom.getChildren("configurator")) { //$NON-NLS-1$ + String configuratorId = configuratorDom.getAttribute("id"); //$NON-NLS-1$ + AbstractProjectConfigurator configurator = LifecycleMappingFactory.getProjectConfigurator(configuratorId); + if(configurator == null) { + String message = "Configurator '" + + configuratorId + + "' is not available for project '" + + pom.getProject().getName() + + "'. To enable full functionality, install the configurator and run Maven->Update Project Configuration."; + MavenPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, IMavenConstants.PLUGIN_ID, message)); + MavenPlugin.getDefault().getConsole().logError(message); +// throw new IllegalArgumentException(message); + } else { + custmizableMapping.addConfigurator(configurator); + } + } + } + + if(executionsDom != null) { + for(Xpp3Dom execution : executionsDom.getChildren("mojoExecution")) { //$NON-NLS-1$ + String strRunOnIncremental = execution.getAttribute("runOnIncremental"); //$NON-NLS-1$ + custmizableMapping.addConfigurator(MojoExecutionProjectConfigurator.fromString(execution.getValue(), + toBool(strRunOnIncremental, true))); + } + } + } + + if (lifecycleMapping == null) { + // Do not create error marker here - it is created in ProjectConfigurationManager.validateLifecycleMappingConfiguration + String msg = "Project " + pom.getProject().getName() + " uses unknown or missing lifecycle mapping with id='" + + mappingId + "', project packaging type='" + project.getPackaging() + "'."; + //Exception e = new Exception(msg); + //MavenLogger.log(msg, e); + console.logError(msg); + return new MissingLifecycleMapping(mappingId); + } + + return lifecycleMapping; + } + + private boolean toBool(String value, boolean def) { + if(value == null || value.length() == 0) { + return def; + } + return Boolean.parseBoolean(value); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryReader.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryReader.java new file mode 100644 index 00000000..56452fae --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryReader.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; +import org.osgi.service.packageadmin.PackageAdmin; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.osgi.service.resolver.VersionRange; + +import org.codehaus.plexus.util.IOUtil; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * Workspace state reader + * + * @author Eugene Kuleshov + */ +public class ProjectRegistryReader { + + private static final String WORKSPACE_STATE = "workspaceState.ser"; //$NON-NLS-1$ + + private final File stateFile; + + private static PackageAdmin packageAdmin; + + public ProjectRegistryReader(File stateLocationDir) { + this.stateFile = new File(stateLocationDir, WORKSPACE_STATE); + } + + public ProjectRegistry readWorkspaceState(final ProjectRegistryManager managerImpl) { + if(stateFile.exists()) { + final PackageAdmin packageAdmin = getPackageAdmin(); + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new BufferedInputStream(new FileInputStream(stateFile))) { + { + enableResolveObject(true); + } + + protected Object resolveObject(Object o) throws IOException { + if(o instanceof IPathReplace) { + return ((IPathReplace) o).getPath(); + } else if(o instanceof IFileReplace) { + return ((IFileReplace) o).getFile(); + } else if(o instanceof MavenProjectManagerImplReplace) { + return managerImpl; + } + return super.resolveObject(o); + } + + protected java.lang.Class<?> resolveClass(java.io.ObjectStreamClass desc) throws IOException, + ClassNotFoundException { + String symbolicName = (String) readObject(); + if(symbolicName == null) { + return super.resolveClass(desc); + } + String versionStr = (String) readObject(); + Version version = Version.parseVersion(versionStr); + VersionRange versionRange = new VersionRange(version, true, version, true); + Bundle[] bundles = packageAdmin.getBundles(symbolicName, versionRange.toString()); + if(bundles == null || bundles.length != 1) { + throw new ClassNotFoundException("Could not find bundle " + symbolicName + "/" + version //$NON-NLS-1$ //$NON-NLS-2$ + + " required to load class " + desc.getName()); //$NON-NLS-1$ + } + return bundles[0].loadClass(desc.getName()); + }; + }; + return (ProjectRegistry) is.readObject(); + } catch(Exception ex) { + MavenLogger.log("Can't read workspace state", ex); + } finally { + IOUtil.close(is); + } + } + return null; + } + + private static synchronized PackageAdmin getPackageAdmin() { + // TODO inject dependencies already! + if(packageAdmin == null) { + BundleContext context = MavenPlugin.getDefault().getBundleContext(); + ServiceReference serviceReference = context.getServiceReference(PackageAdmin.class.getName()); + packageAdmin = (PackageAdmin) context.getService(serviceReference); + } + return packageAdmin; + } + + public void writeWorkspaceState(ProjectRegistry state) { + final ClassLoader thisClassloader = getClass().getClassLoader(); + + final PackageAdmin packageAdmin = getPackageAdmin(); + + ObjectOutputStream os = null; + try { + os = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(stateFile))) { + { + enableReplaceObject(true); + } + + protected Object replaceObject(Object o) throws IOException { + if(o instanceof IPath) { + return new IPathReplace((IPath) o); + } else if(o instanceof IFile) { + return new IFileReplace((IFile) o); + } else if(o instanceof ProjectRegistryManager) { + return new MavenProjectManagerImplReplace(); + } + return super.replaceObject(o); + } + + protected void annotateClass(java.lang.Class<?> cl) throws IOException { + // if the class is visible through this classloader, assume it will be during reading stream back + try { + Class<?> target = cl; + while(target.isArray()) { + target = target.getComponentType(); + } + + if(target.isPrimitive() || target.equals(thisClassloader.loadClass(target.getName()))) { + writeObject(null); // TODO is there a better way? + return; + } + } catch(ClassNotFoundException ex) { + // fall through + } + + // foreign class + Bundle bundle = packageAdmin.getBundle(cl); + if(bundle != null) { + writeObject(bundle.getSymbolicName()); + writeObject(bundle.getVersion().toString()); + } + + // TODO this will likely fail during desirialization + }; + }; + synchronized(state) { // see MNGECLIPSE-860 + os.writeObject(state); + } + } catch(Exception ex) { + MavenLogger.log("Can't write workspace state", ex); + } finally { + IOUtil.close(os); + } + } + + /** + * IPath replacement used for object serialization + */ + private static final class IPathReplace implements Serializable { + private static final long serialVersionUID = -2361259525684491181L; + + private final String path; + + public IPathReplace(IPath path) { + this.path = path.toPortableString(); + } + + public IPath getPath() { + return Path.fromPortableString(path); + } + } + + /** + * IFile replacement used for object serialization + */ + private static final class IFileReplace implements Serializable { + private static final long serialVersionUID = -7266001068347075329L; + + private final String path; + + public IFileReplace(IFile file) { + this.path = file.getFullPath().toPortableString(); + } + + public IFile getFile() { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + return root.getFile(Path.fromPortableString(path)); + } + } + + static final class MavenProjectManagerImplReplace implements Serializable { + private static final long serialVersionUID = 1995671440438776471L; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java new file mode 100644 index 00000000..ff0eeb51 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.eclipse.ui.progress.IProgressConstants; + +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.jobs.IBackgroundProcessingQueue; +import org.eclipse.m2e.core.project.MavenUpdateRequest; + +public class ProjectRegistryRefreshJob extends Job implements IResourceChangeListener, IPreferenceChangeListener, IBackgroundProcessingQueue { + + private static final int DELTA_FLAGS = IResourceDelta.CONTENT | IResourceDelta.MOVED_FROM | IResourceDelta.MOVED_TO + | IResourceDelta.COPIED_FROM | IResourceDelta.REPLACED; + + private final List<MavenUpdateRequest> queue = new ArrayList<MavenUpdateRequest>(); + + private final ProjectRegistryManager manager; + + private final IMavenConfiguration mavenConfiguration; + + private final MavenConsole console; + + public ProjectRegistryRefreshJob(ProjectRegistryManager manager, MavenConsole console, IMavenConfiguration mavenConfiguration) { + super(Messages.ProjectRegistryRefreshJob_title); + this.manager = manager; + this.mavenConfiguration = mavenConfiguration; + this.console = console; + setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + } + + public void refresh(MavenUpdateRequest updateRequest) { + queue(updateRequest); + schedule(1000L); + } + + // Job + + public IStatus run(IProgressMonitor monitor) { + monitor.beginTask(Messages.ProjectRegistryRefreshJob_task_refreshing, IProgressMonitor.UNKNOWN); + ArrayList<MavenUpdateRequest> requests; + synchronized(this.queue) { + requests = new ArrayList<MavenUpdateRequest>(this.queue); + this.queue.clear(); + } + + try { + MutableProjectRegistry newState = manager.newMutableProjectRegistry(); + try { + + for (MavenUpdateRequest request : requests) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + manager.refresh(newState, request, monitor); + } + + ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRoot(); + getJobManager().beginRule(rule, monitor); + try { + manager.applyMutableProjectRegistry(newState, monitor); + } finally { + getJobManager().endRule(rule); + } + } finally { + newState.close(); + } + + } catch(CoreException ex) { + MavenLogger.log(ex); + + } catch(OperationCanceledException ex) { + console.logMessage("Refreshing Maven model is canceled"); + + } catch (StaleMutableProjectRegistryException e) { + synchronized(this.queue) { + this.queue.addAll(0, requests); + if(!this.queue.isEmpty()) { + schedule(1000L); + } + } + + } catch(Exception ex) { + MavenLogger.log(ex.getMessage(), ex); + + } finally { + monitor.done(); + + } + + return Status.OK_STATUS; + } + + // IResourceChangeListener + + public void resourceChanged(IResourceChangeEvent event) { + boolean offline = mavenConfiguration.isOffline(); + boolean updateSnapshots = false; + + int type = event.getType(); + + if(IResourceChangeEvent.PRE_CLOSE == type || IResourceChangeEvent.PRE_DELETE == type) { + queue(new MavenUpdateRequest((IProject) event.getResource(), // + offline, updateSnapshots)); + + } else { + // if (IResourceChangeEvent.POST_CHANGE == type) + IResourceDelta delta = event.getDelta(); // workspace delta + IResourceDelta[] projectDeltas = delta.getAffectedChildren(); + Set<IProject> removeProjects = new LinkedHashSet<IProject>(); + Set<IProject> refreshProjects = new LinkedHashSet<IProject>(); + for(int i = 0; i < projectDeltas.length; i++ ) { + try { + projectChanged(projectDeltas[i], removeProjects, refreshProjects); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + + // XXX consider to run refresh in offline mode when it is triggered by resource change + if(!removeProjects.isEmpty()) { + IProject[] projects = removeProjects.toArray(new IProject[removeProjects.size()]); + MavenUpdateRequest updateRequest = new MavenUpdateRequest(projects, offline, updateSnapshots); + updateRequest.setForce(false); + queue(updateRequest); + console.logMessage("Refreshing " + updateRequest.toString()); + } + if(!refreshProjects.isEmpty()) { + IProject[] projects = refreshProjects.toArray(new IProject[refreshProjects.size()]); + MavenUpdateRequest updateRequest = new MavenUpdateRequest(projects, offline, updateSnapshots); + updateRequest.setForce(false); + queue(updateRequest); + console.logMessage("Refreshing " + updateRequest.toString()); + } + } + + synchronized(queue) { + if(!queue.isEmpty()) { + schedule(1000L); + } + } + } + + private void projectChanged(IResourceDelta delta, Set<IProject> removeProjects, final Set<IProject> refreshProjects) + throws CoreException { + final IProject project = (IProject) delta.getResource(); + + for(IPath path : ProjectRegistryManager.METADATA_PATH) { + if (delta.findMember(path) != null) { + removeProjects.add(project); + return; + } + } + + delta.accept(new IResourceDeltaVisitor() { + public boolean visit(IResourceDelta delta) { + IResource resource = delta.getResource(); + if(resource instanceof IFile && IMavenConstants.POM_FILE_NAME.equals(resource.getName())) { + // XXX ignore output folders + if(delta.getKind() == IResourceDelta.REMOVED + || delta.getKind() == IResourceDelta.ADDED + || (delta.getKind() == IResourceDelta.CHANGED && ((delta.getFlags() & DELTA_FLAGS) != 0))) + { + // XXX check for interesting resources + refreshProjects.add(project); + } + } + return true; + } + }); + } + + private void queue(MavenUpdateRequest command) { + synchronized(queue) { + queue.add(command); + } + } + + public void preferenceChange(PreferenceChangeEvent event) { + boolean offline = mavenConfiguration.isOffline(); + boolean updateSnapshots = false; + + if (event.getSource() instanceof IProject) { + queue(new MavenUpdateRequest(new IProject[] {(IProject) event.getSource()}, offline, updateSnapshots)); + } + } + + public boolean isEmpty() { + synchronized(queue) { + return queue.isEmpty(); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/RequiredCapability.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/RequiredCapability.java new file mode 100644 index 00000000..0adf2681 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/RequiredCapability.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.Serializable; + + +/** + * RequiredCapability + * + * @author igor + */ +public abstract class RequiredCapability implements Serializable { + + private static final long serialVersionUID = -5445156687502685383L; + + private final VersionlessKey versionlessKey; + + public RequiredCapability(String namepsace, String id) { + if(namepsace == null || id == null) { + throw new NullPointerException(); + } + this.versionlessKey = new VersionlessKey(namepsace, id); + } + + public VersionlessKey getVersionlessKey() { + return versionlessKey; + } + + /** + * Returns true if provided capability *potentially* satisfies this requirement. Capability/requirement match will be + * used to check if workspace project changes (new/changed/remove projects and metadata changes) affect other + * projects. isPotentialMatch Implementations should be good enough to avoid obviously pointless project dependency + * refreshes, but does not have to be perfectly precise. + */ + public abstract boolean isPotentialMatch(Capability capability); + + protected static <T> boolean eq(T a, T b) { + return a != null ? a.equals(b) : b == null; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/StaleMutableProjectRegistryException.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/StaleMutableProjectRegistryException.java new file mode 100644 index 00000000..43f8ecd9 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/StaleMutableProjectRegistryException.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +/** + * StaleMutableProjectRegistryException + * + * @author igor + */ +public class StaleMutableProjectRegistryException extends IllegalStateException { + + private static final long serialVersionUID = -1456658363775761949L; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/VersionlessKey.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/VersionlessKey.java new file mode 100644 index 00000000..55cef303 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/VersionlessKey.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.project.registry; + +import java.io.Serializable; + + +/** + * VersionlessKey + * + * @author igor + */ +public class VersionlessKey implements Serializable { + private static final long serialVersionUID = 2125977578206347429L; + + private final String namespace; + + private final String id; + + public VersionlessKey(String namespace, String id) { + if(namespace == null || id == null) { + throw new NullPointerException(); + } + this.namespace = namespace; + this.id = id; + } + + public String getNamespace() { + return namespace; + } + + public String getId() { + return id; + } + + public int hashCode() { + int hash = namespace.hashCode(); + hash = hash * 17 + id.hashCode(); + return hash; + } + + public boolean equals(Object obj) { + if(obj == this) { + return true; + } + if(!(obj instanceof VersionlessKey)) { + return false; + } + VersionlessKey other = (VersionlessKey) obj; + return namespace.equals(other.namespace) && id.equals(other.id); + } + + public String toString() { + return namespace + "/" + id; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryDiscoverer.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryDiscoverer.java new file mode 100644 index 00000000..4dfbb56d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryDiscoverer.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.repository; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * IRepositoryDiscoverer + * + * @author igor + */ +public interface IRepositoryDiscoverer { + + /** + * Called during updateRegistry operation. + */ + public void addRepositories(RepositoryRegistry registry, IProgressMonitor monitor) throws CoreException; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryIndexer.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryIndexer.java new file mode 100644 index 00000000..1573b657 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/IRepositoryIndexer.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.repository; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.repository.IRepository; + +/** + * IRepositoryIndexer + * + * @author igor + */ +public interface IRepositoryIndexer { + + public String getIndexerId(); + + /** + * This method is called from a background thread which does not keep any workspace locks. + */ + public void initialize(IProgressMonitor monitor) throws CoreException; + + /** + * Called by repository registry when new repository is added. + * + * This method is called from a background thread which does not keep any workspace locks. + */ + public void repositoryAdded(IRepository repository, IProgressMonitor monitor) throws CoreException; + + /** + * Called by repository registry when a repository is removed. + * + * This method is called from a background thread which does not keep any workspace locks. + */ + public void repositoryRemoved(IRepository repository, IProgressMonitor monitor) throws CoreException; +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryInfo.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryInfo.java new file mode 100644 index 00000000..a84aac55 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryInfo.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.repository; + +import java.io.File; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.IPath; + +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.wagon.authentication.AuthenticationInfo; + +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + +public class RepositoryInfo implements IRepository { + + private final String id; + private final String repositoryUrl; + private final File basedir; + private final int scope; + private final AuthenticationInfo authInfo; + + private String uid; + + private String mirrorId; + private String mirrorOf; + private Set<IPath> projects = new HashSet<IPath>(); + + public RepositoryInfo(String id, String repositoryUrl, int scope, AuthenticationInfo authInfo) { + this(id, repositoryUrl, getBasedir(repositoryUrl), scope, authInfo); + } + + public RepositoryInfo(String id, String repositoryUrl, File basedir, int scope, AuthenticationInfo authInfo) { + this.id = id; + this.repositoryUrl = repositoryUrl; + this.scope = scope; + this.authInfo = authInfo; + this.basedir = basedir; + } + + public AuthenticationInfo getAuthenticationInfo() { + return authInfo; + } + + public String getUrl() { + return repositoryUrl; + } + + public String getId() { + return id; + } + + public String getMirrorId() { + return mirrorId; + } + + public String getMirrorOf() { + return mirrorOf; + } + + public void setMirrorOf(String mirrorOf) { + this.mirrorOf = mirrorOf; + } + + public void setMirrorId(String mirrorId) { + this.mirrorId = mirrorId; + } + + public Set<IPath> getProjects() { + return projects; + } + + public void addProject(IPath project) { + if (isScope(IRepositoryRegistry.SCOPE_PROJECT)) { + projects.add(project); + } + } + + public void removeProject(IPath project) { + projects.remove(project); + } + + public String getUid() { + if (uid == null) { + uid = getUid(id, repositoryUrl, authInfo != null? authInfo.getUserName(): null); + } + + return uid; + } + + public static String getUid(String id, String repositoryUrl, String username) { + StringBuilder sb = new StringBuilder(); + if (id != null) { + sb.append(id); + } + sb.append('|').append(repositoryUrl); + if (username != null) { + sb.append('|').append(username); + } + String uid; + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); //$NON-NLS-1$ + digest.update(sb.toString().getBytes()); + byte messageDigest[] = digest.digest(); + StringBuffer hexString = new StringBuffer(); + for(int i = 0; i < messageDigest.length; i++ ) { + String hex = Integer.toHexString(0xFF & messageDigest[i]); + if(hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + uid = hexString.toString(); + } catch(NoSuchAlgorithmException ex) { + //this shouldn't happen with MD5 + uid = sb.toString(); + uid = uid.replace(':', '_').replace('/', '_').replace('|','_'); + } + return uid; + } + + public String getProtocol() { + return getProtocol(repositoryUrl); + } + + // copy&paste from MavenArtifactRepository#protocol + public static String getProtocol(String repositoryUrl) { + final int pos = repositoryUrl.indexOf(":"); //$NON-NLS-1$ + + if(pos == -1) { + return "file"; //$NON-NLS-1$ + } + return repositoryUrl.substring(0, pos).trim(); + } + + public static File getBasedir(String repositoryUrl) { + if (getProtocol(repositoryUrl).equalsIgnoreCase("file")) { //$NON-NLS-1$ + // dirty trick! + MavenArtifactRepository trick = new MavenArtifactRepository(); + trick.setUrl(repositoryUrl); + return new File(trick.getBasedir()); + } + return null; + } + + public File getBasedir() { + return basedir; + } + + public boolean isScope(int scope) { + return (this.scope & scope) != 0; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (id != null) { + sb.append(id).append('|'); + } + sb.append(repositoryUrl); + return sb.toString(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistry.java new file mode 100644 index 00000000..03179291 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistry.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.repository; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.settings.Mirror; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.Settings; +import org.apache.maven.wagon.authentication.AuthenticationInfo; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactRepositoryRef; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.ISettingsChangeListener; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + + +/** + * RepositoryRegistry + * + * @author igor + */ +public class RepositoryRegistry implements IRepositoryRegistry, IMavenProjectChangedListener, ISettingsChangeListener { + + private final IMaven maven; + + private final MavenProjectManager projectManager; + + /** + * Maps repositoryUrl to IndexInfo of repository index + */ + private final Map<String, RepositoryInfo> repositories = new ConcurrentHashMap<String, RepositoryInfo>(); + + /** + * Lazy instantiated local repository instance. + */ + private RepositoryInfo localRepository; + + /** + * Lock guarding lazy instantiation of localRepository instance + */ + private final Object localRepositoryLock = new Object(); + + private final RepositoryInfo workspaceRepository; + + private ArrayList<IRepositoryIndexer> indexers = new ArrayList<IRepositoryIndexer>(); + + private ArrayList<IRepositoryDiscoverer> discoverers = new ArrayList<IRepositoryDiscoverer>(); + + private final RepositoryRegistryUpdateJob job = new RepositoryRegistryUpdateJob(this); + + public RepositoryRegistry(IMaven maven, MavenProjectManager projectManager) { + this.maven = maven; + this.projectManager = projectManager; + + this.workspaceRepository = new RepositoryInfo(null/*id*/, "workspace://"/*url*/, null/*basedir*/, SCOPE_WORKSPACE, null/*auth*/); //$NON-NLS-1$ + } + + private RepositoryInfo newLocalRepositoryInfo() { + File localBasedir = new File(maven.getLocalRepositoryPath()); + try { + localBasedir = localBasedir.getCanonicalFile(); + } catch (IOException e) { + // will never happen + localBasedir = localBasedir.getAbsoluteFile(); + } + + String localUrl; + try { + localUrl = localBasedir.toURL().toExternalForm(); + } catch(MalformedURLException ex) { + MavenLogger.log("Could not parse local repository path", ex); + localUrl = "file://" + localBasedir.getAbsolutePath(); //$NON-NLS-1$ + } + + // initialize local and workspace repositories + RepositoryInfo localRepository = new RepositoryInfo(null/*id*/, localUrl, localBasedir, SCOPE_LOCAL, null/*auth*/); + return localRepository; + } + + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + /* + * This method is called while holding workspace lock. Avoid long-running operations if possible. + */ + + Settings settings = null; + try { + settings = maven.getSettings(); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + + for(MavenProjectChangedEvent event : events) { + IMavenProjectFacade oldFacade = event.getOldMavenProject(); + if (oldFacade != null) { + removeProjectRepositories(oldFacade, monitor); + } + IMavenProjectFacade facade = event.getMavenProject(); + if(facade != null) { + try { + addProjectRepositories(settings, facade, null /*asyncUpdate*/); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + + private void addProjectRepositories(Settings settings, IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException { + ArrayList<ArtifactRepositoryRef> repositories = getProjectRepositories(facade); + + for (ArtifactRepositoryRef repo : repositories) { + RepositoryInfo repository = getRepository(repo); + if (repository != null) { + repository.addProject(facade.getPom().getFullPath()); + continue; + } + AuthenticationInfo auth = getAuthenticationInfo(settings, repo.getId()); + repository = new RepositoryInfo(repo.getId(), repo.getUrl(), SCOPE_PROJECT, auth); + repository.addProject(facade.getPom().getFullPath()); + + addRepository(repository, monitor); + } + } + + public void addRepository(RepositoryInfo repository, IProgressMonitor monitor) { + if (!repositories.containsKey(repository.getUid())) { + repositories.put(repository.getUid(), repository); + + for (IRepositoryIndexer indexer : indexers) { + try { + indexer.repositoryAdded(repository, monitor); + } catch (CoreException e) { + MavenLogger.log(e); + } + } + } + } + + private void removeProjectRepositories(IMavenProjectFacade facade, IProgressMonitor monitor) { + ArrayList<ArtifactRepositoryRef> repositories = getProjectRepositories(facade); + + for (ArtifactRepositoryRef repo : repositories) { + RepositoryInfo repository = getRepository(repo); + if (repository != null && repository.isScope(SCOPE_PROJECT)) { + repository.removeProject(facade.getPom().getFullPath()); + if (repository.getProjects().isEmpty()) { + removeRepository(repository, monitor); + } + } + } + } + + private void removeRepository(RepositoryInfo repository, IProgressMonitor monitor) { + repositories.remove(repository.getUid()); + + for (IRepositoryIndexer indexer : indexers) { + try { + indexer.repositoryRemoved(repository, monitor); + } catch (CoreException e) { + MavenLogger.log(e); + } + } + } + + private ArrayList<ArtifactRepositoryRef> getProjectRepositories(IMavenProjectFacade facade) { + ArrayList<ArtifactRepositoryRef> repositories = new ArrayList<ArtifactRepositoryRef>(); + repositories.addAll(facade.getArtifactRepositoryRefs()); + repositories.addAll(facade.getPluginArtifactRepositoryRefs()); + return repositories; + } + + + public AuthenticationInfo getAuthenticationInfo(Settings settings, String id) throws CoreException { + if (settings == null) { + return null; + } + + Server server = settings.getServer(id); + if (server == null || server.getUsername() == null) { + return null; + } + + server = maven.decryptPassword(server); + + AuthenticationInfo info = new AuthenticationInfo(); + info.setUserName(server.getUsername()); + info.setPassword(server.getPassword()); + return info; + } + + public void updateRegistry(IProgressMonitor monitor) throws CoreException { + Settings settings = maven.getSettings(); + List<Mirror> mirrors = maven.getMirrors(); + + // initialize indexers + for (IRepositoryIndexer indexer : indexers) { + indexer.initialize(monitor); + } + + // process configured repositories + + Map<String, RepositoryInfo> oldRepositories = new HashMap<String, RepositoryInfo>(repositories); + repositories.clear(); + + addRepository(this.workspaceRepository, monitor); + + synchronized(localRepositoryLock) { + this.localRepository = newLocalRepositoryInfo(); + } + addRepository(this.localRepository, monitor); + + // mirrors + for(Mirror mirror : mirrors) { + AuthenticationInfo auth = getAuthenticationInfo(settings, mirror.getId()); + RepositoryInfo repository = new RepositoryInfo(mirror.getId(), mirror.getUrl(), SCOPE_SETTINGS, auth); + repository.setMirrorOf(mirror.getMirrorOf()); + addRepository(repository, monitor); + } + + // repositories from settings.xml + ArrayList<ArtifactRepository> repos = new ArrayList<ArtifactRepository>(); + repos.addAll(maven.getArtifactRepositories(false)); + repos.addAll(maven.getPluginArtifactRepositories(false)); + + for(ArtifactRepository repo : repos) { + Mirror mirror = maven.getMirror(repo); + AuthenticationInfo auth = getAuthenticationInfo(settings, repo.getId()); + RepositoryInfo repository = new RepositoryInfo(repo.getId(), repo.getUrl(), SCOPE_SETTINGS, auth); + if (mirror != null) { + repository.setMirrorId(mirror.getId()); + } + addRepository(repository, monitor); + } + + // project-specific repositories + for (IMavenProjectFacade facade : projectManager.getProjects()) { + addProjectRepositories(settings, facade, monitor); + } + + // custom repositories + for (IRepositoryDiscoverer discoverer : discoverers) { + discoverer.addRepositories(this, monitor); + } + + oldRepositories.keySet().removeAll(repositories.keySet()); + for (RepositoryInfo repository : oldRepositories.values()) { + removeRepository(repository, monitor); + } + } + + public List<IRepository> getRepositories(int scope) { + ArrayList<IRepository> result = new ArrayList<IRepository>(); + for (RepositoryInfo repository : repositories.values()) { + if (repository.isScope(scope)) { + result.add(repository); + } + } + return result; + } + + public void updateRegistry() { + job.updateRegistry(); + } + + public void addRepositoryIndexer(IRepositoryIndexer indexer) { + this.indexers.add(indexer); + } + + public void addRepositoryDiscoverer(IRepositoryDiscoverer discoverer) { + this.discoverers.add(discoverer); + } + + public RepositoryInfo getRepository(ArtifactRepositoryRef ref) { + String uid = RepositoryInfo.getUid(ref.getId(), ref.getUrl(), ref.getUsername()); + return repositories.get(uid); + } + + public IRepository getWorkspaceRepository() { + return workspaceRepository; + } + + public IRepository getLocalRepository() { + synchronized(localRepositoryLock) { + if(localRepository == null) { + localRepository = newLocalRepositoryInfo(); + } + } + + return localRepository; + } + + public void settingsChanged(Settings settings) { + updateRegistry(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistryUpdateJob.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistryUpdateJob.java new file mode 100644 index 00000000..9cf09960 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/repository/RepositoryRegistryUpdateJob.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.internal.repository; + +import java.util.ArrayList; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.jobs.IBackgroundProcessingQueue; + +/** + * RepositoryRegistryUpdateJob + * + * @author igor + */ +public class RepositoryRegistryUpdateJob extends Job implements IBackgroundProcessingQueue { + + private final RepositoryRegistry registry; + + private final ArrayList<Object> queue = new ArrayList<Object>(); + + public RepositoryRegistryUpdateJob(RepositoryRegistry registry) { + super(Messages.RepositoryRegistryUpdateJob_title); + this.registry = registry; + } + + public IStatus run(IProgressMonitor monitor) { + synchronized(queue) { + queue.clear(); + } + try { + registry.updateRegistry(monitor); + } catch(CoreException ex) { + return ex.getStatus(); + } + return Status.OK_STATUS; + } + + public boolean isEmpty() { + synchronized(queue) { + return queue.isEmpty(); + } + } + + public void updateRegistry() { + synchronized(queue) { + queue.add(new Object()); + schedule(1000L); + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/jobs/IBackgroundProcessingQueue.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/jobs/IBackgroundProcessingQueue.java new file mode 100644 index 00000000..bc9e3986 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/jobs/IBackgroundProcessingQueue.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.jobs; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + + +/** + * Common interface implemented by all m2e background processing jobs. + * + * @author igor + */ +public interface IBackgroundProcessingQueue { + public void join() throws InterruptedException; + + public boolean isEmpty(); + + public IStatus run(IProgressMonitor monitor); + + public boolean cancel(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/messages.properties b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/messages.properties new file mode 100644 index 00000000..14080ec8 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/messages.properties @@ -0,0 +1,152 @@ +# +# Copyright (c) 2007, 2008 Sonatype, Inc. +# 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 +# + +action.moduleProjectWizardAction=New Maven Module Project +artifactComponent.artifact=Artifact +artifactComponent.artifactId=Artifact Id\: +artifactComponent.description=Description\: +artifactComponent.groupId=Group Id\: +artifactComponent.name=Name\: +artifactComponent.package=Package\: +artifactComponent.packaging=Packaging\: +artifactComponent.version=Version\: +directoriesComponent.projectLayout=Project layout +launch.browseFs=Browse File Syste&m... +launch.browseVariables=&Variables... +launch.browseWorkspace=Browse &Workspace... +launch.choosePomDir=Choose &Base directory +launch.errorPomMissing=Base directory doesn't exist +launch.errorSelectPom=Please select Base directory first +launch.goals=&Select... +launch.goalsDialog.lifecycleBuild=Build Lifecycle Phases +launch.goalsDialog.lifecycleClean=Clean Lifecycle Phases +launch.goalsDialog.lifecycleSite=Site Lifecycle Phases +launch.goalsDialog.title=Goals +launch.goalsLabel=&Goals\: +launch.mainTabName=Main +launch.pomDirectoryDoesntExist=Base directory doesn't exist or can't be read +launch.pomDirectoryEmpty=Base directory should be specified +launch.pomGroup=Base directory: +launch.profilesLabel=&Profiles\: +launch.propAddButton=&Add... +launch.propName=Parameter Name +launch.propRemoveButton=&Remove +launch.propEditButton=E&dit... +launch.propValue=Value +launch.propertyDialog.browseVariables=Varia&bles... +launch.propertyDialog.name=Name\: +launch.propertyDialog.value=Value\: +locationComponent.atExternal=Create project at e&xternal location +locationComponent.browse=B&rowse... +locationComponent.directory=&Location\: +locationComponent.inWorkspace=Create project in &workspace +locationComponent.location=Location +locationComponent.selectLocation=Select Location +plugin.markerArtifactResolutionError=Artifact resolution error\: +plugin.markerBuildError=Project build error\: {0} +plugin.markerParsingError=Parsing error\: +preferences.checksumPolicyFail=fail +preferences.checksumPolicyIgnore=ignore +preferences.checksumPolicyWarn=warning +preferences.debugOutput=Debu&g Output +preferences.downloadSources=Do&wnload Artifact Sources +preferences.downloadJavadoc=Download Artifact &JavaDoc +preferences.globalChecksumPolicy=Global Checksum &Policy\: +preferences.globalSettingsFile=&Global Settings File\: +preferences.goalOnImport=Goals to run on project i&mport\: +preferences.goalOnUpdate=Goals to run when &updating project configuration\: +preferences.localRepository=Local Repository\: +preferences.offline=&Offline +preferences.refreshButton=Re&fresh Settings +preferences.reindexButton=Re&index +preferences.userSettingsFile=User Settings File\: +projectSelectionDialog.title=Select a Maven project +resolverConfiguration.advanced=Ad&vanced +resolverConfiguration.enableResourceFiltering=Enable &resource filtering +resolverConfiguration.profiles=&Profiles\: +resolverConfiguration.projectsForModules=&Separate projects for modules +resolverConfiguration.resolveWorkspaceProjects=Resolve &Workspace projects +resolverConfiguration.template=Name &template\: +resolverConfiguration.templateDescription=Optional Eclipse project name template, e.g. "[groupId].[artifactId]-[version]" +resolverConfiguration.useMavenOutputFolders=Use Maven &output folders +wizard.import.page.browse=&Browse... +wizard.import.page.deselectAll=&Deselect All +wizard.import.page.projects=&Projects\: +wizard.import.page.refresh=&Refresh +wizard.import.page.root=&Root Directory\: +wizard.import.page.scanningErrors=Scanning errors ({0})\: +wizard.import.page.selectAll=Select &All +wizard.import.page.selectRootFolder=Select Root Folder +wizard.import.validator.projectExists=Project {0} already exists\n Add a version or custom suffix using "Name template" in "Advanced" settings +wizard.import.validator.workspaceFolder=Can't import project {0} from an existing workspace folder +wizard.module.page.archetype.title=New Maven Module +wizard.module.page.artifact.title=New Maven Module +wizard.module.page.parameters.title=New Maven Module +wizard.module.page.parent.browse=Br&owse... +wizard.module.page.parent.description=Select the module name and parent +wizard.module.page.parent.moduleName=&Module Name\: +wizard.module.page.parent.parentProject=Parent Project\: +wizard.module.page.parent.title=New Maven Module +wizard.module.page.parent.validator.moduleName=Enter a module name. +wizard.module.page.parent.validator.nameExists=A resource with this name already exists. +wizard.module.page.parent.validator.parentProject=Select a parent project. +wizard.module.page.title=New Maven module +wizard.module.title=New Maven Module +wizard.project.error.pomAlreadyExists=A pom.xml file already exists in the destination folder. +wizard.project.error.pomExists=POM already exists +wizard.project.job.creating=Creating {0} +wizard.project.job.creatingProject=Creating project "{0}" +wizard.project.job.failed=Failed to create project "{0}" +wizard.project.page.archetype.archetypes=Archetypes\: +wizard.project.page.archetype.column.artifactId=Artifact Id +wizard.project.page.archetype.column.groupId=Group Id +wizard.project.page.archetype.column.version=Version +wizard.project.page.archetype.description=Select an Archetype +wizard.project.page.archetype.details=Archetype Details +wizard.project.page.archetype.details.description=Description\: +wizard.project.page.archetype.details.repository=Repository\: +wizard.project.page.archetype.retrievingArchetypes=Retrieving archetypes\: +wizard.project.page.archetype.title=New Maven project +wizard.project.page.artifact.parent.artifactId=Artifact Id\: +wizard.project.page.artifact.parent.browse=Browse... +wizard.project.page.artifact.parent.clear=Clear +wizard.project.page.artifact.parent.groupId=Group Id\: +wizard.project.page.artifact.parent.title=Parent Project +wizard.project.page.artifact.parent.version=Version\: +wizard.project.page.dependencies.add=&Add... +wizard.project.page.dependencies.dependencies=Maven dependencies\: +wizard.project.page.dependencies.description=Add additional dependencies to the project. +wizard.project.page.dependencies.remove=&Remove +wizard.project.page.dependencies.title=Select additional dependencies +wizard.project.page.maven2.archetype.parameters.description=Specify Archetype parameters +wizard.project.page.maven2.description=Configure project +wizard.project.page.maven2.title=New Maven project +wizard.project.page.maven2.validator.artifactID=Enter an artifact id. +wizard.project.page.maven2.validator.artifactIDnospaces=Artifact id cannot contain spaces. +wizard.project.page.maven2.validator.artifactIDinvalid=Invalid artifact id: {0} +wizard.project.page.maven2.validator.groupID=Enter a group id for the artifact. +wizard.project.page.maven2.validator.groupIDnospaces=Group id cannot contain spaces. +wizard.project.page.maven2.validator.groupIDinvalid=Invalid group id: {0} +wizard.project.page.maven2.validator.projectNameInvalid=Invalid project name: {0} +wizard.project.page.maven2.validator.packaging=Enter a packaging for the artifact. +wizard.project.page.maven2.validator.parent=To specify a parent project, set the parent group id, artifact id and version. +wizard.project.page.maven2.validator.requiredProperty=Required property "{0}" is not set. +wizard.project.page.maven2.validator.version=Enter a version for the artifact. +wizard.project.page.project.description=Select project name and location +wizard.project.page.project.projectName=&Project name\: +wizard.project.page.project.simpleProject=Create a &simple project (skip archetype selection) +wizard.project.page.project.title=New Maven project +wizard.project.page.project.validator.invalidLocation=Invalid project location path +wizard.project.page.project.validator.projectExists=Project "{0}" already exists. +wizard.project.page.project.validator.projectLocation=Enter a location for the project. +wizard.project.page.project.validator.projectName=Enter a project name. +wizard.project.title=New Maven Project +jira.username=Jira username (will use default user if empty) +jira.password=Jira password +pomEditor.defaultPage=Open XML page in the POM editor by default +pomEditor.showAdvancedTabs=Show advanced tabs in the POM editor
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java new file mode 100644 index 00000000..c996a869 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; + + +/** + * Project Scanner + * + * @author Eugene Kuleshov + */ +public abstract class AbstractProjectScanner<T extends MavenProjectInfo> { + + private final List<T> projects = new ArrayList<T>(); + private final List<Throwable> errors = new ArrayList<Throwable>(); + + /** + * Returns <code>List</code> of {@link MavenProjectInfo} + */ + public List<T> getProjects() { + return projects; + } + + /** + * Returns <code>List</code> of <code>Exception</code> + */ + public List<Throwable> getErrors() { + return this.errors; + } + + protected void addProject(T mavenProjectInfo) { + projects.add(mavenProjectInfo); + } + + protected void addError(Throwable exception) { + errors.add(exception); + } + + public abstract String getDescription(); + + public abstract void run(IProgressMonitor monitor) throws InterruptedException; +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenMarkerManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenMarkerManager.java new file mode 100644 index 00000000..a96622ba --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenMarkerManager.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; + +import org.apache.maven.execution.MavenExecutionResult; + +/** + * IMavenMarkerManager + * + * @author Fred Bricon + */ +public interface IMavenMarkerManager { + + /** + * Add markers to a pom file from a MavenExecutionResult. + * @param pomFile the pom file to attach markers to. + * @param result containing messages to be addedd as markers + */ + public void addMarkers(IResource pomFile, String type, MavenExecutionResult result); + + /** + * Add a Maven marker to a resource + * @param resource : the IResource to attach the marker to. + * @param message : the marker's message. + * @param lineNumber : the resource line to attach the marker to. + * @param severity : the severity of the marker. + */ + public IMarker addMarker(IResource resource, String type, String message, int lineNumber, int severity); + + /** + * Delete all Maven markers of the specified type from an IResource + */ + public void deleteMarkers(IResource resource, String type) throws CoreException; + + /** + * Transform an exception into an error marker on an IResource + */ + public void addErrorMarkers(IResource resource, String type, Exception ex); + + public void addEditorHintMarkers(IResource pom, String type); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectChangedListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectChangedListener.java new file mode 100644 index 00000000..f37ec18d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectChangedListener.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.runtime.IProgressMonitor; + +public interface IMavenProjectChangedListener { + /** + * This method is called while holding workspace lock. + */ + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectFacade.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectFacade.java new file mode 100644 index 00000000..ded501aa --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectFacade.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.ArtifactRef; +import org.eclipse.m2e.core.embedder.ArtifactRepositoryRef; +import org.eclipse.m2e.core.internal.project.ProjectConfigurationManager; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + +/** + * IMavenProjectFacade + * + * @noimplement This interface is not intended to be implemented by clients. + * + * @author Igor Fedorenko + */ +public interface IMavenProjectFacade { + + /** + * Returns project relative paths of resource directories + */ + IPath[] getResourceLocations(); + + /** + * Returns project relative paths of test resource directories + */ + IPath[] getTestResourceLocations(); + + IPath[] getCompileSourceLocations(); + + IPath[] getTestCompileSourceLocations(); + + /** + * Returns project resource for given file system location or null the location is outside of project. + * + * @param resourceLocation absolute file system location + * @return IPath the full, absolute workspace path resourceLocation + */ + IPath getProjectRelativePath(String resourceLocation); + + /** + * Returns the full, absolute path of this project maven build output directory relative to the workspace or null if + * maven build output directory cannot be determined or outside of the workspace. + */ + IPath getOutputLocation(); + + /** + * Returns the full, absolute path of this project maven build test output directory relative to the workspace or null + * if maven build output directory cannot be determined or outside of the workspace. + */ + IPath getTestOutputLocation(); + + IPath getFullPath(); + + /** + * Lazy load and cache MavenProject instance + */ + MavenProject getMavenProject(IProgressMonitor monitor) throws CoreException; + + /** + * Returns cached MavenProject instance associated with this facade or null, + * if the cache has not been populated yet. + */ + MavenProject getMavenProject(); + + /** + * Lazy load and cache build execution plan + */ + MavenExecutionPlan getExecutionPlan(IProgressMonitor monitor) throws CoreException; + + String getPackaging(); + + IProject getProject(); + + IFile getPom(); + + File getPomFile(); + + /** + * Returns the full, absolute path of the given file relative to the workspace. Returns null if the file does not + * exist or is not a member of this project. + */ + IPath getFullPath(File file); + + /** + * Visits trough Maven project artifacts and modules + * + * @param visitor a project visitor used to visit Maven project + * @param flags flags to specify visiting behavior. See {@link IMavenProjectVisitor#LOAD}, + * {@link IMavenProjectVisitor#NESTED_MODULES}. + */ + void accept(IMavenProjectVisitor visitor, int flags) throws CoreException; + + void accept(IMavenProjectVisitor2 visitor, int flags, IProgressMonitor monitor) throws CoreException; + + List<String> getMavenProjectModules(); + + Set<ArtifactRef> getMavenProjectArtifacts(); + + ResolverConfiguration getResolverConfiguration(); + + /** + * @return true if maven project needs to be re-read from disk + */ + boolean isStale(); + + ArtifactKey getArtifactKey(); + + /** + * Associates the value with the key in session (i.e. transient) context. + * Intended as a mechanism to cache state derived from MavenProject. + * Session properties are cleared when MavenProject is re-read from disk. + * + * @see #getSessionProperty(String) + */ + public void setSessionProperty(String key, Object value); + + /** + * @return the value associated with the key in session context or null + * if the key is not associated with any value. + * + * @see #setSessionProperty(String, Object) + */ + public Object getSessionProperty(String key); + + public Set<ArtifactRepositoryRef> getArtifactRepositoryRefs(); + + public Set<ArtifactRepositoryRef> getPluginArtifactRepositoryRefs(); + + public ILifecycleMapping getLifecycleMapping(IProgressMonitor monitor) throws CoreException; + + /** + * Returns true if the project configuration is valid. This flag is set by + * {@link ProjectConfigurationManager#validateProjectConfiguration(IMavenProjectFacade, IProgressMonitor)}. Returns + * false if the project configuration has not been validated yet. + */ + boolean hasValidConfiguration(); + + void setHasValidConfiguration(boolean hasValidConfiguration); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectImportResult.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectImportResult.java new file mode 100644 index 00000000..218e5a4d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectImportResult.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.resources.IProject; + +/** + * Holds IProject that was created during project import + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMavenProjectImportResult { + + /** + * @return MavenProjectInfo maven project import request + */ + MavenProjectInfo getMavenProjectInfo(); + + /** + * @return IProject imported project or <code>null</code> if the project could not be imported. + */ + IProject getProject(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor.java new file mode 100644 index 00000000..71a5e400 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.runtime.CoreException; + +/** + * This interface is implemented by clients that visit MavenProject tree. + */ +public interface IMavenProjectVisitor { + + public static int NONE = 0; + + public static int LOAD = 1 << 0; + + /** + * Visit Maven project or project module + * + * @param projectFacade a facade for visited Maven project + * @return true if nested artifacts and modules should be visited + */ + public boolean visit(IMavenProjectFacade projectFacade) throws CoreException; + + /** + * Visit Maven project dependency/artifact + * + * @param projectFacade a facade for visited Maven project + * @param artifact an artifact for project dependency + */ +// public void visit(IMavenProjectFacade projectFacade, Artifact artifact); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor2.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor2.java new file mode 100644 index 00000000..5effc055 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IMavenProjectVisitor2.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.apache.maven.artifact.Artifact; + +/** + * IMavenProjectVisitor2 + * + * @author Igor Fedorenko + */ +public interface IMavenProjectVisitor2 extends IMavenProjectVisitor { + + /** + * @param mavenProjectFacade + * @param artifact + */ + void visit(IMavenProjectFacade mavenProjectFacade, Artifact artifact); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IProjectConfigurationManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IProjectConfigurationManager.java new file mode 100644 index 00000000..a36aa966 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/IProjectConfigurationManager.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + + +public interface IProjectConfigurationManager { + + ISchedulingRule getRule(); + + List<IMavenProjectImportResult> importProjects(Collection<MavenProjectInfo> projects, // + ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException; + + void createSimpleProject(IProject project, IPath location, Model model, String[] folders, + ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException; + + void createArchetypeProject(IProject project, IPath location, Archetype archetype, // + String groupId, String artifactId, String version, String javaPackage, Properties properties, // + ProjectImportConfiguration configuration, IProgressMonitor monitor) throws CoreException; + + Set<MavenProjectInfo> collectProjects(Collection<MavenProjectInfo> projects); + + void enableMavenNature(IProject project, ResolverConfiguration configuration, IProgressMonitor monitor) + throws CoreException; + + void disableMavenNature(IProject project, IProgressMonitor monitor) throws CoreException; + + void updateProjectConfiguration(IProject project, ResolverConfiguration configuration, IProgressMonitor monitor) + throws CoreException; + + ILifecycleMapping getLifecycleMapping(IMavenProjectFacade projectFacade, IProgressMonitor monitor) + throws CoreException; + + /** + * Validates that the project configuration is valid. It does not actually (re)configure the project, but it validates + * that the project configure action will not fail for obvious reasons like missing lifecycle mapping, missing project + * configuration, etc. + * + * @return true if the configuration is valid + */ + boolean validateProjectConfiguration(IMavenProjectFacade projectFacade, IProgressMonitor monitor); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/LocalProjectScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/LocalProjectScanner.java new file mode 100644 index 00000000..b397910e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/LocalProjectScanner.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubProgressMonitor; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Profile; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * @author Eugene Kuleshov + */ +public class LocalProjectScanner extends AbstractProjectScanner<MavenProjectInfo> { + private final File workspaceRoot; + private final List<String> folders; + private final boolean basedirRemameRequired; + + private Set<File> scannedFolders = new HashSet<File>(); + private final MavenConsole console; + private final MavenModelManager modelManager; + + public LocalProjectScanner(File workspaceRoot, String folder, boolean needsRename, MavenModelManager modelManager, + MavenConsole console) { + this(workspaceRoot, Collections.singletonList(folder), needsRename, modelManager, console); + } + + public LocalProjectScanner(File workspaceRoot, List<String> folders, boolean basedirRemameRequired, + MavenModelManager modelManager, MavenConsole console) { + this.workspaceRoot = workspaceRoot; + this.folders = folders; + this.basedirRemameRequired = basedirRemameRequired; + this.modelManager = modelManager; + this.console = console; + } + + public void run(IProgressMonitor monitor) throws InterruptedException { + monitor.beginTask(Messages.LocalProjectScanner_task_scanning, IProgressMonitor.UNKNOWN); + try { + for(String folderName : folders) { + try { + File folder = new File(folderName).getCanonicalFile(); + scanFolder(folder, new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN)); + } catch(IOException ex) { + addError(ex); + } + } + } finally { + monitor.done(); + } + } + + private void scanFolder(File baseDir, IProgressMonitor monitor) throws InterruptedException { + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + monitor.subTask(baseDir.toString()); + monitor.worked(1); + + // Don't scan the .metadata folder + if(!baseDir.exists() || !baseDir.isDirectory() || IMavenConstants.METADATA_FOLDER.equals(baseDir.getName())) { + return; + } + + MavenProjectInfo projectInfo = readMavenProjectInfo(baseDir, "", null); //$NON-NLS-1$ + if(projectInfo != null) { + addProject(projectInfo); + return; // don't scan subfolders of the Maven project + } + + File[] files = baseDir.listFiles(); + for(int i = 0; i < files.length; i++ ) { + File file; + try { + file = files[i].getCanonicalFile(); + if(file.isDirectory()) { + scanFolder(file, monitor); + } + } catch(IOException ex) { + addError(ex); + } + } + } + + private MavenProjectInfo readMavenProjectInfo(File baseDir, String modulePath, MavenProjectInfo parentInfo) { + try { + baseDir = baseDir.getCanonicalFile(); + + File pomFile = new File(baseDir, IMavenConstants.POM_FILE_NAME); + if(!pomFile.exists()) { + return null; + } + + Model model = modelManager.readMavenModel(pomFile); + + if (!scannedFolders.add(baseDir)) { + return null; // we already know this project + } + + String pomName = modulePath + "/" + IMavenConstants.POM_FILE_NAME; //$NON-NLS-1$ + + MavenProjectInfo projectInfo = newMavenProjectInfo(pomName, pomFile, model, parentInfo); + projectInfo.setBasedirRename(getBasedirRename(projectInfo)); + + Map<String, Set<String>> modules = new LinkedHashMap<String, Set<String>>(); + for(String module : model.getModules()) { + modules.put(module, new HashSet<String>()); + } + + for(Profile profile : model.getProfiles()) { + for(String module : profile.getModules()) { + Set<String> profiles = modules.get(module); + if(profiles == null) { + profiles = new HashSet<String>(); + modules.put(module, profiles); + } + profiles.add(profile.getId()); + } + } + + for(Map.Entry<String, Set<String>> e : modules.entrySet()) { + String module = e.getKey(); + Set<String> profiles = e.getValue(); + + File moduleBaseDir = new File(baseDir, module); + MavenProjectInfo moduleInfo = readMavenProjectInfo(moduleBaseDir, module, projectInfo); + if(moduleInfo != null) { + moduleInfo.addProfiles(profiles); + projectInfo.add(moduleInfo); + } + } + + return projectInfo; + + } catch(CoreException ex) { + addError(ex); + console.logError("Unable to read model " + baseDir.getAbsolutePath()); + } catch(IOException ex) { + addError(ex); + console.logError("Unable to read model " + baseDir.getAbsolutePath()); + } + + return null; + } + + protected MavenProjectInfo newMavenProjectInfo(String label, File pomFile, Model model, MavenProjectInfo parent) { + return new MavenProjectInfo(label, pomFile, model, parent); + } + + public String getDescription() { + return folders.toString(); + } + + private int getBasedirRename(MavenProjectInfo mavenProjectInfo) throws IOException { + File cannonical = mavenProjectInfo.getPomFile().getParentFile().getParentFile().getCanonicalFile(); + if (basedirRemameRequired && cannonical.equals(workspaceRoot.getCanonicalFile())) { + return MavenProjectInfo.RENAME_REQUIRED; + } + return MavenProjectInfo.RENAME_NO; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectChangedEvent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectChangedEvent.java new file mode 100644 index 00000000..e0d775ef --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectChangedEvent.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.resources.IFile; + +public class MavenProjectChangedEvent { + + private final IFile source; + + private final int kind; + + private final int flags; + + public static final int KIND_ADDED = 1; + + public static final int KIND_REMOVED = 2; + + public static final int KIND_CHANGED = 3; + + public static final int FLAG_NONE = 0; + + public static final int FLAG_DEPENDENCIES = 1; + + public static final int FLAG_DEPENDENCY_SOURCES = 2; + + public static final int FLAG_ENTRY_SOURCES = 3; + + private final IMavenProjectFacade oldMavenProject; + + private final IMavenProjectFacade mavenProject; + + public MavenProjectChangedEvent(IFile source, int kind, int flags, IMavenProjectFacade oldMavenProject, IMavenProjectFacade mavenProject) { + this.source = source; + this.kind = kind; + this.flags = flags; + this.oldMavenProject = oldMavenProject; + this.mavenProject = mavenProject; + } + + public int getKind() { + return kind; + } + + public int getFlags() { + return flags; + } + + public IMavenProjectFacade getMavenProject() { + return mavenProject; + } + + public IMavenProjectFacade getOldMavenProject() { + return oldMavenProject; + } + + public IFile getSource() { + return source; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectInfo.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectInfo.java new file mode 100644 index 00000000..64088a15 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectInfo.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * @author Eugene Kuleshov + */ +public class MavenProjectInfo { + + /** + * Project basedir must NOT be renamed on filesystem. + */ + public static final int RENAME_NO = 0; + + /** + * Project basedir MUST be ranamed to match workspace project name. + */ + public static final int RENAME_REQUIRED = 2; + + private final String label; + + private File pomFile; + + private Model model; + + private final MavenProjectInfo parent; + + /** + * Map of MavenProjectInfo + */ + private final Map<String, MavenProjectInfo> projects = new LinkedHashMap<String, MavenProjectInfo>(); + + private final Set<String> profiles = new HashSet<String>(); + + private int basedirRename = RENAME_NO; + + public MavenProjectInfo(String label, File pomFile, Model model, MavenProjectInfo parent) { + this.label = label; + this.pomFile = pomFile; + this.model = model; + this.parent = parent; + } + + public void setPomFile(File pomFile) { + File oldDir = this.pomFile.getParentFile(); + File newDir = pomFile.getParentFile(); + + for(MavenProjectInfo projectInfo : projects.values()) { + File childPom = projectInfo.getPomFile(); + if(isSubDir(oldDir, childPom.getParentFile())) { + String oldPath = oldDir.getAbsolutePath(); + String path = childPom.getAbsolutePath().substring(oldPath.length()); + projectInfo.setPomFile(new File(newDir, path)); + } + } + + this.pomFile = pomFile; + } + + /** @deprecated use set/get BasedirRename */ + public void setNeedsRename(boolean needsRename) { + setBasedirRename(needsRename? RENAME_REQUIRED: RENAME_NO); + } + + /** @deprecated use set/get BasedirRenamePolicy */ + public boolean isNeedsRename() { + return getBasedirRename() == RENAME_REQUIRED; + } + + /** + * See {@link #RENAME_NO}, {@link #RENAME_REQUIRED} + */ + public void setBasedirRename(int basedirRename) { + this.basedirRename = basedirRename; + } + + /** + * See {@link #RENAME_NO}, {@link #RENAME_REQUIRED} + */ + public int getBasedirRename() { + return basedirRename; + } + + private boolean isSubDir(File parentDir, File subDir) { + if(parentDir.equals(subDir)) { + return true; + } + + if(subDir.getParentFile()!=null) { + return isSubDir(parentDir, subDir.getParentFile()); + } + + return false; + } + + public void add(MavenProjectInfo info) { + String key; + try { + if(info.getPomFile() == null) { + // Is this possible? + key = info.getLabel(); + } else { + key = info.getPomFile().getCanonicalPath(); + } + } catch(IOException ex) { + throw new RuntimeException(ex); + } + MavenProjectInfo i = projects.get(key); + if(i==null) { + projects.put(key, info); + } else { + MavenLogger.log("Project info " + this + " already has a child project info with key '" + key + "'"); //$NON-NLS-3$ + for(Iterator<String> it = info.getProfiles().iterator(); it.hasNext();) { + i.addProfile(it.next()); + } + } + } + + public void addProfile(String profileId) { + if(profileId!=null) { + this.profiles.add(profileId); + } + } + + public void addProfiles(Collection<String> profiles) { + this.profiles.addAll(profiles); + } + + public String getLabel() { + return this.label; + } + + public File getPomFile() { + return this.pomFile; + } + + public Model getModel() { + return this.model; + } + + public void setModel(Model model) { + this.model = model; + } + + public Collection<MavenProjectInfo> getProjects() { + return this.projects.values(); + } + + public MavenProjectInfo getParent() { + return this.parent; + } + + public Set<String> getProfiles() { + return this.profiles; + } + + public boolean equals(Object obj) { + if(obj instanceof MavenProjectInfo) { + MavenProjectInfo info = (MavenProjectInfo) obj; + if(pomFile == null) { + return info.getPomFile() == null; + } + return pomFile.equals(info.getPomFile()); + } + return false; + } + + public int hashCode() { + return pomFile==null ? 0 : pomFile.hashCode(); + } + + public String toString() { + return "'" + label + "'" + (pomFile == null ? "" : " " + pomFile.getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectManager.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectManager.java new file mode 100644 index 00000000..303d30a7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectManager.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; + +import org.apache.maven.artifact.repository.MavenArtifactRepository; +import org.apache.maven.execution.MavenExecutionRequest; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.internal.project.registry.ProjectRegistryManager; +import org.eclipse.m2e.core.internal.project.registry.ProjectRegistryRefreshJob; + + +/** + * This class keeps track of all Maven projects present in the workspace and provides mapping between Maven artifacts + * and Workspace projects. + */ +public class MavenProjectManager { + + public static final String STATE_FILENAME = "workspacestate.properties"; //$NON-NLS-1$ + + private final ProjectRegistryManager manager; + + private final ProjectRegistryRefreshJob mavenBackgroundJob; + + private final File workspaceStateFile; + + public MavenProjectManager(ProjectRegistryManager manager, ProjectRegistryRefreshJob mavenBackgroundJob, File stateLocation) { + this.manager = manager; + this.mavenBackgroundJob = mavenBackgroundJob; + this.workspaceStateFile = new File(stateLocation, STATE_FILENAME); + } + + // Maven projects + + /** + * Performs requested Maven project update asynchronously, using background + * job. This method returns immediately. + */ + public void refresh(MavenUpdateRequest request) { + mavenBackgroundJob.refresh(request); + } + + /** + * Performs requested Maven project update synchronously. In other words, this method + * does not return until all affected projects have been updated and + * corresponding MavenProjectChangeEvent's broadcast. + * + * This method acquires a lock on the workspace's root. + */ + public void refresh(MavenUpdateRequest request, IProgressMonitor monitor) throws CoreException { + manager.refresh(request, monitor); + } + + public void addMavenProjectChangedListener(IMavenProjectChangedListener listener) { + manager.addMavenProjectChangedListener(listener); + } + + public void removeMavenProjectChangedListener(IMavenProjectChangedListener listener) { + manager.removeMavenProjectChangedListener(listener); + } + + /** + * Returns MavenProjectFacade corresponding to the pom. This method first looks in the project cache, then attempts to + * load the pom if the pom is not found in the cache. In the latter case, workspace resolution is assumed to be + * enabled for the pom but the pom will not be added to the cache. + */ + public IMavenProjectFacade create(IFile pom, boolean load, IProgressMonitor monitor) { + return manager.create(pom, load, monitor); + } + + public IMavenProjectFacade create(IProject project, IProgressMonitor monitor) { + return manager.create(project, monitor); + } + + public ResolverConfiguration getResolverConfiguration(IProject project) { + IMavenProjectFacade projectFacade = create(project, new NullProgressMonitor()); + if(projectFacade!=null) { + return projectFacade.getResolverConfiguration(); + } + return manager.readResolverConfiguration(project); + } + + public boolean setResolverConfiguration(IProject project, ResolverConfiguration configuration) { + return manager.setResolverConfiguration(project, configuration); + } + + /** + * @return MavenProjectFacade[] all maven projects which exist under workspace root + */ + public IMavenProjectFacade[] getProjects() { + return manager.getProjects(); + } + + /** + * @return IMavenProjectFacade cached IMavenProjectFacade corresponding + * to the project or null if there is no cache entry for the project. + */ + public IMavenProjectFacade getProject(IProject project) { + return manager.getProject(project); + } + + public IMavenProjectFacade getMavenProject(String groupId, String artifactId, String version) { + return manager.getMavenProject(groupId, artifactId, version); + } + + public File getWorkspaceStateFile() { + return workspaceStateFile; + } + + /** + * Request full maven build for a project. + * + * This call only has effect for projects that have maven nature and + * Maven builder configured. + * + * This call does not trigger the build. Instead next time Maven builder + * processes the project it will use goals to execute during clean + * build regardless of the build type requested. + * + * The main purpose of this call is to allow coordination between multiple + * builders configured for the same project. + */ + public void requestFullMavenBuild(IProject project) throws CoreException { + project.setSessionProperty(IMavenConstants.FULL_MAVEN_BUILD, Boolean.TRUE); + } + + /** + * PROVISIONAL + */ + public MavenExecutionRequest createExecutionRequest(IFile pom, ResolverConfiguration resolverConfiguration, IProgressMonitor monitor) throws CoreException { + return manager.createExecutionRequest(pom, resolverConfiguration, monitor); + } + + /** + * PROVISIONAL + */ + public MavenExecutionRequest createExecutionRequest(IMavenProjectFacade project, IProgressMonitor monitor) throws CoreException { + return manager.createExecutionRequest(project.getPom(), project.getResolverConfiguration(), monitor); + } + + /** + * Local repository implementation that checks artifacts in workspace first. + * + * PROVISIONAL + */ + public MavenArtifactRepository getWorkspaceLocalRepository() throws CoreException { + return manager.getWorkspaceLocalRepository(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectPomScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectPomScanner.java new file mode 100644 index 00000000..fe73112f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectPomScanner.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.osgi.util.NLS; + +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +import org.apache.maven.model.Profile; +import org.apache.maven.model.Scm; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Maven project scanner using dependency list + * + * @author Eugene Kuleshov + */ +public class MavenProjectPomScanner<T> extends AbstractProjectScanner<MavenProjectScmInfo> { + + private final boolean developer; + + private final Dependency[] dependencies; + + private MavenConsole console; + + private IMaven maven; + + public MavenProjectPomScanner(boolean developer, Dependency[] dependencies, // + MavenModelManager modelManager, MavenConsole console) { + this.developer = developer; + this.dependencies = dependencies; + this.console = console; + this.maven = MavenPlugin.getDefault().getMaven(); + } + + public String getDescription() { + if(dependencies.length==1) { + Dependency d = dependencies[0]; + return d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion() + (d.getClassifier()==null ? "" : ":" + d.getClassifier()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + return "" + dependencies.length + " projects"; //$NON-NLS-1$ + } + + public void run(IProgressMonitor monitor) throws InterruptedException { + for(int i = 0; i < dependencies.length; i++ ) { + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + Dependency d = dependencies[i]; + + try { + Model model = resolveModel(d.getGroupId(), d.getArtifactId(), d.getVersion(), monitor); + if(model==null) { + String msg = "Can't resolve " + d.getArtifactId(); + console.logError(msg); + addError(new Exception(msg)); + continue; + } + + Scm scm = resolveScm(model, monitor); + if(scm==null) { + String msg = "No SCM info for " + d.getArtifactId(); + console.logError(msg); + addError(new Exception(msg)); + continue; + } + + String tag = scm.getTag(); + + console.logMessage(d.getArtifactId()); + console.logMessage("Connection: " + scm.getConnection()); + console.logMessage(" dev: " + scm.getDeveloperConnection()); + console.logMessage(" url: " + scm.getUrl()); + console.logMessage(" tag: " + tag); + + String connection; + if(developer) { + connection = scm.getDeveloperConnection(); + if(connection==null) { + String msg = d.getArtifactId() + " doesn't specify developer SCM connection"; + console.logError(msg); + addError(new Exception(msg)); + continue; + } + } else { + connection = scm.getConnection(); + if(connection==null) { + String msg = d.getArtifactId() + " doesn't specify SCM connection"; + console.logError(msg); + addError(new Exception(msg)); + continue; + } + } + + // connection: scm:svn:https://svn.apache.org/repos/asf/incubator/wicket/branches/wicket-1.2.x/wicket + // dev: scm:svn:https://svn.apache.org/repos/asf/incubator/wicket/branches/wicket-1.2.x/wicket + // url: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.2.x/wicket + // tag: HEAD + + // TODO add an option to select all modules/projects and optimize scan + + if(connection.endsWith("/")) { //$NON-NLS-1$ + connection = connection.substring(0, connection.length()-1); + } + + int n = connection.lastIndexOf("/"); //$NON-NLS-1$ + String label = (n == -1 ? connection : connection.substring(n)) + "/" + IMavenConstants.POM_FILE_NAME; //$NON-NLS-1$ + + addProject(new MavenProjectScmInfo(label, model, null, tag, connection, connection)); + + } catch(Exception ex) { + addError(ex); + String msg = "Error reading " + d.getArtifactId(); + console.logError(msg); + MavenLogger.log(msg, ex); + } + } + } + + private Scm resolveScm(Model model, IProgressMonitor monitor) throws ArtifactResolutionException, + ArtifactNotFoundException, XmlPullParserException, IOException, CoreException { + Scm scm = model.getScm(); + if(scm != null) { + return scm; + } + + Parent parent = model.getParent(); + if(parent == null) { + return null; + } + + Model parentModel = resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), monitor); + if(parentModel==null) { + return null; + } + + Scm parentScm = resolveScm(parentModel, monitor); + if(parentScm==null) { + return null; + } + + Set<String> modules = new HashSet<String>(parentModel.getModules()); + List<Profile> parentModelProfiles = parentModel.getProfiles(); + for(Profile profile : parentModelProfiles) { + modules.addAll(profile.getModules()); + } + + // heuristics for matching module names to artifactId + String artifactId = model.getArtifactId(); + for(String module : modules) { + if(module.equals(artifactId) || module.endsWith("/" + artifactId)) { //$NON-NLS-1$ + if(parentScm.getConnection()!=null) { + parentScm.setConnection(parentScm.getConnection() + "/" + module); //$NON-NLS-1$ + } + if(parentScm.getDeveloperConnection()!=null) { + parentScm.setDeveloperConnection(parentScm.getDeveloperConnection() + "/" + module); //$NON-NLS-1$ + } + return parentScm; + } + } + + // XXX read modules from profiles + + return parentScm; + } + + private Model resolveModel(String groupId, String artifactId, String version, IProgressMonitor monitor) + throws CoreException { + monitor.subTask(NLS.bind(Messages.MavenProjectPomScanner_task_resolving, new Object[] { groupId, artifactId, version})); + + List<ArtifactRepository> repositories = maven.getArtifactRepositories(); + Artifact artifact = maven.resolve(groupId, artifactId, version, "pom", null, repositories, monitor); //$NON-NLS-1$ + + File file = artifact.getFile(); + if(file == null) { + return null; + } + + // XXX this fail on reading extensions + // MavenProject project = embedder.readProject(file); + + monitor.subTask(NLS.bind(Messages.MavenProjectPomScanner_23, new Object[] {groupId, artifactId, version})); + return maven.readModel(file); + } + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectScmInfo.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectScmInfo.java new file mode 100644 index 00000000..fc810dfa --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectScmInfo.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; + +import org.apache.maven.model.Model; + + +/** + * @author Eugene Kuleshov + */ +public class MavenProjectScmInfo extends MavenProjectInfo { + + private final String folderUrl; + private final String repositoryUrl; + private final String revision; + private final String branch; + + private String username; + private String password; + + private File sslCertificate; + private String sslCertificatePassphrase; + + public MavenProjectScmInfo(String label, Model model, MavenProjectInfo parent, // + String revision, String folderUrl, String repositoryUrl) { + this(label, model, parent, null, revision, folderUrl, repositoryUrl); + } + + public MavenProjectScmInfo(String label, Model model, MavenProjectInfo parent, // + String branch, String revision, String folderUrl, String repositoryUrl) { + super(label, null, model, parent); + this.revision = revision; + this.folderUrl = folderUrl; + this.repositoryUrl = repositoryUrl; + this.branch = branch; + } + + public String getBranch() { + return this.branch; + } + + public String getRevision() { + return this.revision; + } + + public String getFolderUrl() { + return folderUrl; + } + + public String getRepositoryUrl() { + return repositoryUrl; + } + + public boolean equals(Object obj) { + if(obj instanceof MavenProjectScmInfo) { + MavenProjectScmInfo info = (MavenProjectScmInfo) obj; + return folderUrl.equals(info.getFolderUrl()); + } + return false; + } + + public int hashCode() { + return folderUrl.hashCode(); + } + + public String toString() { + return getLabel() + " " + folderUrl; //$NON-NLS-1$ + } + + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + + public void setSSLCertificate(File certificate) { + this.sslCertificate = certificate; + } + public File getSSLCertificate() { + return sslCertificate; + } + + public String getSSLCertificatePassphrase() { + return sslCertificatePassphrase; + } + public void setSSLCertificatePassphrase(String passphrase) { + this.sslCertificatePassphrase = passphrase; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectUtils.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectUtils.java new file mode 100644 index 00000000..cbcf60e6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenProjectUtils.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.File; +import java.util.LinkedHashSet; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.apache.maven.model.Resource; + +/** + * Collection of helper methods to map between MavenProject and IResource. + * + * @author igor + */ +public class MavenProjectUtils { + + private MavenProjectUtils() { + } + + /** + * Returns project resource for given filesystem location or null the location is outside of project. + * + * @param resourceLocation absolute filesystem location + * @return IPath the full, absolute workspace path resourceLocation + */ + public static IPath getProjectRelativePath(IProject project, String resourceLocation) { + if(resourceLocation == null) { + return null; + } + IPath projectLocation = project.getLocation(); + IPath directory = Path.fromOSString(resourceLocation); // this is an absolute path! + if(projectLocation == null || !projectLocation.isPrefixOf(directory)) { + return null; + } + + return directory.removeFirstSegments(projectLocation.segmentCount()).makeRelative().setDevice(null); + } + + public static IPath[] getResourceLocations(IProject project, List<Resource> resources) { + LinkedHashSet<IPath> locations = new LinkedHashSet<IPath>(); + for(Resource resource : resources) { + locations.add(getProjectRelativePath(project, resource.getDirectory())); + } + return locations.toArray(new IPath[locations.size()]); + } + + public static IPath[] getSourceLocations(IProject project, List<String> roots) { + LinkedHashSet<IPath> locations = new LinkedHashSet<IPath>(); + for(String root : roots) { + IPath path = getProjectRelativePath(project, root); + if(path != null) { + locations.add(path); + } + } + return locations.toArray(new IPath[locations.size()]); + } + + /** + * Returns the full, absolute path of the given file relative to the workspace. Returns null if the file does not + * exist or is not a member of this project. + */ + public static IPath getFullPath(IProject project, File file) { + if (project == null || file == null) { + return null; + } + + IPath projectPath = project.getLocation(); + if(projectPath == null) { + return null; + } + + IPath filePath = new Path(file.getAbsolutePath()); + if (!projectPath.isPrefixOf(filePath)) { + return null; + } + IResource resource = project.findMember(filePath.removeFirstSegments(projectPath.segmentCount())); + if (resource == null) { + return null; + } + return resource.getFullPath(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenUpdateRequest.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenUpdateRequest.java new file mode 100644 index 00000000..8f936f24 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/MavenUpdateRequest.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; + +import org.eclipse.m2e.core.core.IMavenConstants; + +/** + * Maven project update request + * + * @author Eugene Kuleshov + */ +public class MavenUpdateRequest { + private boolean offline = false; + private boolean updateSnapshots = false; + private boolean force = true; + + /** + * Set of {@link IFile} + */ + private final Set<IFile> pomFiles = new LinkedHashSet<IFile>(); + + public MavenUpdateRequest(boolean offline, boolean updateSnapshots) { + this.offline = offline; + this.updateSnapshots = updateSnapshots; + } + + public MavenUpdateRequest(IProject project, boolean offline, boolean updateSnapshots) { + this(offline, updateSnapshots); + addPomFile(project); + } + + public MavenUpdateRequest(IProject[] projects, boolean offline, boolean updateSnapshots) { + this(offline, updateSnapshots); + + for(int i = 0; i < projects.length; i++ ) { + addPomFile(projects[i]); + } + } + + public boolean isOffline() { + return this.offline; + } + + public boolean isUpdateSnapshots() { + return this.updateSnapshots; + } + + public void addPomFiles(Set<IFile> pomFiles) { + for (IFile pomFile : pomFiles) { + addPomFile(pomFile); + } + } + + public void addPomFile(IFile pomFile) { + pomFiles.add(pomFile); + } + + public void addPomFile(IProject project) { + pomFiles.add(project.getFile(IMavenConstants.POM_FILE_NAME)); + + } + + public void removePomFile(IFile pomFile) { + pomFiles.remove(pomFile); + } + + /** + * Returns Set of {@link IFile} + */ + public Set<IFile> getPomFiles() { + return this.pomFiles; + } + + public boolean isEmpty() { + return this.pomFiles.isEmpty(); + } + + public boolean isForce() { + return force; + } + + public void setForce(boolean force) { + this.force = force; + } + + public String toString() { + StringBuilder sb = new StringBuilder("["); //$NON-NLS-1$ + String sep = ""; //$NON-NLS-1$ + for(IFile pomFile : pomFiles) { + sb.append(sep); + sb.append(pomFile.getFullPath()); + sep = ", "; //$NON-NLS-1$ + } + sb.append("]"); //$NON-NLS-1$ + + if(offline) { + sb.append(" offline"); //$NON-NLS-1$ + } + if(updateSnapshots) { + sb.append(" updateSnapshots"); //$NON-NLS-1$ + } + if(force) { + sb.append(" force"); //$NON-NLS-1$ + } + + return sb.toString(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ProjectImportConfiguration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ProjectImportConfiguration.java new file mode 100644 index 00000000..e8f8b8bf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ProjectImportConfiguration.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.IWorkingSet; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.Messages; + + +/** + * Project import configuration bean. + */ +public class ProjectImportConfiguration { + + private static final String GROUP_ID = "\\[groupId\\]"; //$NON-NLS-1$ + + private static final String ARTIFACT_ID = "\\[artifactId\\]"; //$NON-NLS-1$ + + private static final String VERSION = "\\[version\\]"; //$NON-NLS-1$ + + /** resolver configuration bean */ + private ResolverConfiguration resolverConfiguration; + + /** the project name template */ + private String projectNameTemplate = ""; //$NON-NLS-1$ + + private IWorkingSet[] workingSets; + + /** Creates a new configuration. */ + public ProjectImportConfiguration(ResolverConfiguration resolverConfiguration) { + this.resolverConfiguration = resolverConfiguration; + } + + /** Creates a new configuration. */ + public ProjectImportConfiguration() { + this(new ResolverConfiguration()); + } + + /** Returns the resolver configuration bean. */ + public ResolverConfiguration getResolverConfiguration() { + return resolverConfiguration; + } + + /** Sets the project name template. */ + public void setProjectNameTemplate(String projectNameTemplate) { + this.projectNameTemplate = projectNameTemplate; + } + + /** Returns the project name template. */ + public String getProjectNameTemplate() { + return projectNameTemplate; + } + + /** @deprecated UI aspects will be refactored out of core import logic */ + public void setWorkingSet(IWorkingSet workingSet) { + this.workingSets = workingSet == null ? null : new IWorkingSet[]{workingSet}; + } + + /** @deprecated UI aspects will be refactored out of core import logic */ + public void setWorkingSets(IWorkingSet[] workingSets) { + this.workingSets = workingSets; + } + + /** @deprecated UI aspects will be refactored out of core import logic */ + public IWorkingSet[] getWorkingSets() { + return this.workingSets; + } + + /** + * Calculates the project name for the given model. + * + * @deprecated This method does not take into account MavenProjectInfo.basedirRename + */ + public String getProjectName(Model model) { + // XXX should use resolved MavenProject or Model + if(projectNameTemplate.length() == 0) { + return model.getArtifactId(); + } + + String artifactId = model.getArtifactId(); + String groupId = model.getGroupId(); + if(groupId == null && model.getParent() != null) { + groupId = model.getParent().getGroupId(); + } + String version = model.getVersion(); + if(version == null && model.getParent() != null) { + version = model.getParent().getVersion(); + } + + // XXX needs MavenProjectManager update to resolve groupId and version + return projectNameTemplate.replaceAll(GROUP_ID, groupId).replaceAll(ARTIFACT_ID, artifactId).replaceAll(VERSION, + version == null ? "" : version); //$NON-NLS-1$ + } + + /** + * @deprecated This method does not take into account MavenProjectInfo.basedirRename. + * Use IMavenProjectImportResult#getProject instead + */ + public IProject getProject(IWorkspaceRoot root, Model model) { + return root.getProject(getProjectName(model)); + } + + /** + * @deprecated business logic does not belong to a value object + */ + public IStatus validateProjectName(Model model) { + String projectName = getProjectName(model); + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + + // check if the project name is valid + IStatus nameStatus = workspace.validateName(projectName, IResource.PROJECT); + if(!nameStatus.isOK()) { + return nameStatus; + } + + // check if project already exists + if(workspace.getRoot().getProject(projectName).exists()) { + return new Status( IStatus.ERROR, IMavenConstants.PLUGIN_ID, 0, Messages.getString("wizard.project.page.project.validator.projectExists",projectName), null); //$NON-NLS-1$ + } + + return Status.OK_STATUS; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ResolverConfiguration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ResolverConfiguration.java new file mode 100644 index 00000000..8271bd19 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/ResolverConfiguration.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Resolver configuration holder. + * + * TODO need a better name, this configures all aspects of maven project in eclipse, + * not just dependency resolution. + * + * @author Eugene Kuleshov + */ +public class ResolverConfiguration implements Serializable { + private static final long serialVersionUID = 1258510761534886581L; + + private boolean resolveWorkspaceProjects = true; + + private String activeProfiles = ""; //$NON-NLS-1$ + + public boolean shouldResolveWorkspaceProjects() { + return this.resolveWorkspaceProjects; + } + + public String getActiveProfiles() { + return this.activeProfiles; + } + + public List<String> getActiveProfileList() { + if (activeProfiles.trim().length() > 0) { + return Arrays.asList(activeProfiles.split("[,\\s\\|]")); //$NON-NLS-1$ + } + return new ArrayList<String>(); + } + + public void setResolveWorkspaceProjects(boolean resolveWorkspaceProjects) { + this.resolveWorkspaceProjects = resolveWorkspaceProjects; + } + + public void setActiveProfiles(String activeProfiles) { + this.activeProfiles = activeProfiles; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractBuildParticipant.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractBuildParticipant.java new file mode 100644 index 00000000..ebc79a35 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractBuildParticipant.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.execution.MavenSession; + +import org.sonatype.plexus.build.incremental.BuildContext; + +import org.eclipse.m2e.core.internal.builder.InternalBuildParticipant; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * AbstractMavenBuildParticipant + * + * @author igor + */ +public abstract class AbstractBuildParticipant extends InternalBuildParticipant { + + /** + * This method is called during workspace full or incremental build. + */ + public abstract Set<IProject> build(int kind, IProgressMonitor monitor) throws Exception; + + public boolean callOnEmptyDelta() { + return false; + } + + /** + * This method is called during workspace clean build. + */ + @SuppressWarnings("unused") + public void clean(IProgressMonitor monitor) throws CoreException { + // default implementation does nothing + } + + protected IMavenProjectFacade getMavenProjectFacade() { + return super.getMavenProjectFacade(); + } + + protected IResourceDelta getDelta(IProject project) { + return super.getDelta(project); + } + + protected MavenSession getSession() { + return super.getSession(); + } + + protected BuildContext getBuildContext() { + return super.getBuildContext(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractLifecycleMapping.java new file mode 100644 index 00000000..e64db998 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractLifecycleMapping.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; + +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * AbstractLifecycleMapping + * + * @author igor + */ +public abstract class AbstractLifecycleMapping implements ILifecycleMapping { + + private String name; + + private String id; + + private boolean showConfigurators; + + /** + * Calls #configure method of all registered project configurators + */ + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + addMavenBuilder(request.getProject(), monitor); + + for(AbstractProjectConfigurator configurator : getProjectConfigurators(request.getMavenProjectFacade(), monitor)) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + configurator.configure(request, monitor); + } + } + + public void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + for(AbstractProjectConfigurator configurator : getProjectConfigurators(request.getMavenProjectFacade(), monitor)) { + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + configurator.unconfigure(request, monitor); + } + } + + protected static void addMavenBuilder(IProject project, IProgressMonitor monitor) throws CoreException { + IProjectDescription description = project.getDescription(); + + // ensure Maven builder is always the last one + ICommand mavenBuilder = null; + ArrayList<ICommand> newSpec = new ArrayList<ICommand>(); + for(ICommand command : description.getBuildSpec()) { + if(IMavenConstants.BUILDER_ID.equals(command.getBuilderName())) { + mavenBuilder = command; + } else { + newSpec.add(command); + } + } + if(mavenBuilder == null) { + mavenBuilder = description.newCommand(); + mavenBuilder.setBuilderName(IMavenConstants.BUILDER_ID); + } + newSpec.add(mavenBuilder); + description.setBuildSpec(newSpec.toArray(new ICommand[newSpec.size()])); + + project.setDescription(description, monitor); + } + + /** + * @return Returns the name. + */ + public String getName() { + return this.name; + } + + /** + * @param name The name to set. + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return Returns the id. + */ + public String getId() { + return this.id; + } + + /** + * @param id The id to set. + */ + public void setId(String id) { + this.id = id; + } + + /** + * @param show Set whether the project configurators should show. Default is true. + */ + public void setShowConfigurators(boolean show) { + this.showConfigurators = show; + } + + /** + * Returns whether the project configurators will be shown in the UI. Default is true. + */ + public boolean showConfigurators() { + return this.showConfigurators; + } + + protected List<AbstractBuildParticipant> getBuildParticipants(IMavenProjectFacade facade, + List<AbstractProjectConfigurator> configurators, IProgressMonitor monitor) throws CoreException { + List<AbstractBuildParticipant> participants = new ArrayList<AbstractBuildParticipant>(); + + for(MojoExecution execution : facade.getExecutionPlan(monitor).getMojoExecutions()) { + for(AbstractProjectConfigurator configurator : configurators) { + if(configurator.isSupportedExecution(execution)) { + AbstractBuildParticipant participant = configurator.getBuildParticipant(execution); + if(participant != null) { + participants.add(participant); + } + } + } + } + + return participants; + } + + public List<MojoExecution> getNotCoveredMojoExecutions(IMavenProjectFacade mavenProjectFacade, + IProgressMonitor monitor) throws CoreException { + List<MojoExecution> result = new ArrayList<MojoExecution>(); + + List<AbstractProjectConfigurator> projectConfigurators = getProjectConfigurators(mavenProjectFacade, monitor); + MavenExecutionPlan mavenExecutionPlan = mavenProjectFacade.getExecutionPlan(monitor); + List<MojoExecution> allMojoExecutions = mavenExecutionPlan.getMojoExecutions(); + for(MojoExecution mojoExecution : allMojoExecutions) { + if(!isInterestingPhase(mojoExecution.getLifecyclePhase())) { + continue; + } + boolean isCovered = false; + for(AbstractProjectConfigurator configurator : projectConfigurators) { + if(configurator.isSupportedExecution(mojoExecution)) { + isCovered = true; + break; + } + } + if(!isCovered) { + result.add(mojoExecution); + } + } + return result; + } + + public List<AbstractBuildParticipant> getBuildParticipants(IMavenProjectFacade facade, IProgressMonitor monitor) + throws CoreException { + List<AbstractProjectConfigurator> configurators = getProjectConfigurators(facade, monitor); + + return getBuildParticipants(facade, configurators, monitor); + } + + private static final String[] INTERESTING_PHASES = {"validate", // + "initialize", // + "generate-sources", // + "process-sources", // + "generate-resources", // + "process-resources", // + "compile", // + "process-classes", // + "generate-test-sources", // + "process-test-sources", // + "generate-test-resources", // + "process-test-resources", // + "test-compile", // + "process-test-classes", // + // "test", // + // "prepare-package", // + // "package", // + //"pre-integration-test", // + // "integration-test", // + // "post-integration-test", // + // "verify", // + // "install", // + // "deploy", // + }; + + public boolean isInterestingPhase(String phase) { + for(String interestingPhase : INTERESTING_PHASES) { + if(interestingPhase.equals(phase)) { + return true; + } + } + return false; + } + + public abstract List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade mavenProjectFacade, + IProgressMonitor monitor) throws CoreException; + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractProjectConfigurator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractProjectConfigurator.java new file mode 100644 index 00000000..d6d51c76 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/AbstractProjectConfigurator.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.PluginExecution; +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.IMavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectManager; + + +/** + * Used to configure maven projects. + * + * @author Igor Fedorenko + */ +public abstract class AbstractProjectConfigurator implements IExecutableExtension, IMavenProjectChangedListener { + + public static final String ATTR_ID = "id"; //$NON-NLS-1$ + + public static final String ATTR_PRIORITY = "priority"; //$NON-NLS-1$ + + public static final String ATTR_NAME = "name"; //$NON-NLS-1$ + + public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + + private int priority; + + private String id; + + private String name; + + /** + * List of maven plugin goal patterns for which this project configurator is enabled automatically. Can be null, in + * which case the project configurator can only be enabled explicitly in pom.xml. + */ + protected List<PluginExecutionFilter> pluginExecutionFilters; + + protected MavenProjectManager projectManager; + + protected IMavenConfiguration mavenConfiguration; + + protected IMavenMarkerManager markerManager; + + protected MavenConsole console; + + protected IMaven maven = MavenPlugin.getDefault().getMaven(); + + public void setProjectManager(MavenProjectManager projectManager) { + this.projectManager = projectManager; + } + + public void setMavenConfiguration(IMavenConfiguration mavenConfiguration) { + this.mavenConfiguration = mavenConfiguration; + } + + public void setMarkerManager(IMavenMarkerManager markerManager) { + this.markerManager = markerManager; + } + + public void setConsole(MavenConsole console) { + this.console = console; + } + + /** + * Configures Eclipse project passed in ProjectConfigurationRequest, using information from Maven project and other + * configuration request parameters + * <p> + * <i>Should be implemented by subclass</i> + * + * @param request a project configuration request + * @param monitor a progress monitor + */ + public abstract void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException; + + /** + * Removes Maven specific configuration from the project passed in ProjectConfigurationRequest + * + * @param request a project un-configuration request + * @param monitor a progress monitor + */ + @SuppressWarnings("unused") + public void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + } + + /** + * Updates project configuration according project changes. + * <p> + * <i>Can be overwritten by subclass</i> + * + * @param event a project change event + * @param monitor a progress monitor + */ + @SuppressWarnings("unused") + public void mavenProjectChanged(MavenProjectChangedEvent event, IProgressMonitor monitor) throws CoreException { + } + + // IMavenProjectChangedListener + + public final void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + for(int i = 0; i < events.length; i++ ) { + try { + mavenProjectChanged(events[i], monitor); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + + public int getPriority() { + return priority; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + // IExecutableExtension + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + this.id = config.getAttribute(ATTR_ID); + this.name = config.getAttribute(ATTR_NAME); + String priorityString = config.getAttribute(ATTR_PRIORITY); + try { + priority = Integer.parseInt(priorityString); + } catch(Exception ex) { + priority = Integer.MAX_VALUE; + } + + IConfigurationElement[] mojos = config.getChildren("mojo"); //$NON-NLS-1$ + if(mojos != null && mojos.length > 0) { + pluginExecutionFilters = new ArrayList<PluginExecutionFilter>(); + for(IConfigurationElement mojo : mojos) { + String groupId = mojo.getAttribute("groupId"); //$NON-NLS-1$ + String artifactId = mojo.getAttribute("artifactId"); //$NON-NLS-1$ + String versionRange = mojo.getAttribute("versionRange"); //$NON-NLS-1$ + String goals = mojo.getAttribute("goals"); //$NON-NLS-1$ + addPluginExecutionFilter(groupId, artifactId, versionRange, goals); + } + } + } + + protected void addPluginExecutionFilter(String groupId, String artifactId, String versionRange, String goals) { + addPluginExecutionFilter(new PluginExecutionFilter(groupId, artifactId, versionRange, goals)); + } + + public void addPluginExecutionFilter(PluginExecutionFilter filter) { + // TODO validate + if(pluginExecutionFilters == null) { + pluginExecutionFilters = new ArrayList<PluginExecutionFilter>(); + } + pluginExecutionFilters.add(filter); + } + + // TODO move to a helper + public static void addNature(IProject project, String natureId, IProgressMonitor monitor) throws CoreException { + if(!project.hasNature(natureId)) { + IProjectDescription description = project.getDescription(); + String[] prevNatures = description.getNatureIds(); + String[] newNatures = new String[prevNatures.length + 1]; + System.arraycopy(prevNatures, 0, newNatures, 1, prevNatures.length); + newNatures[0] = natureId; + description.setNatureIds(newNatures); + project.setDescription(description, monitor); + } + } + + @Deprecated + protected <T> T getParameterValue(MavenSession session, MojoExecution execution, String parameter, Class<T> asType) + throws CoreException { + return maven.getMojoParameterValue(session, execution, parameter, asType); + } + + protected <T> T getParameterValue(String parameter, Class<T> asType, MavenSession session, MojoExecution mojoExecution) + throws CoreException { + PluginExecution execution = new PluginExecution(); + execution.setConfiguration(mojoExecution.getConfiguration()); + return maven.getMojoParameterValue(parameter, asType, session, mojoExecution.getPlugin(), execution, + mojoExecution.getGoal()); + } + + protected void assertHasNature(IProject project, String natureId) throws CoreException { + if(project.getNature(natureId) == null) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + Messages.AbstractProjectConfigurator_error_missing_nature + natureId, null)); + } + } + + @Override + public String toString() { + return id + ":" + name + "(" + priority + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public AbstractBuildParticipant getBuildParticipant(MojoExecution execution) { + return null; + } + + public boolean isSupportedExecution(MojoExecution mojoExecution) { + if(pluginExecutionFilters == null) { + return false; + } + for(PluginExecutionFilter key : pluginExecutionFilters) { + if(key.match(mojoExecution)) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/CustomizableLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/CustomizableLifecycleMapping.java new file mode 100644 index 00000000..762ffd04 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/CustomizableLifecycleMapping.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * @author igor + */ +public class CustomizableLifecycleMapping extends AbstractLifecycleMapping { + public static final String EXTENSION_ID = "customizable"; //$NON-NLS-1$ + + private List<AbstractProjectConfigurator> configurators = new ArrayList<AbstractProjectConfigurator>(); + + @SuppressWarnings("unused") + public List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade facade, IProgressMonitor monitor) + throws CoreException { + return configurators; + } + + public void addConfigurator(AbstractProjectConfigurator configurator) { + this.configurators.add(configurator); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/DefaultLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/DefaultLifecycleMapping.java new file mode 100644 index 00000000..3f5c2f00 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/DefaultLifecycleMapping.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.internal.lifecycle.LifecycleMappingFactory; +import org.eclipse.m2e.core.project.IMavenProjectFacade; + + +/** + * DefaultLifecycleMapping + * + * @author igor + */ +public class DefaultLifecycleMapping extends CustomizableLifecycleMapping { + + private static class MojoExecutionKey { + private final MojoExecution execution; + + public MojoExecutionKey(MojoExecution execution) { + this.execution = execution; + } + + public MojoExecution getMojoExecution() { + return execution; + } + + public int hashCode() { + int hash = execution.getGroupId().hashCode(); + hash = 17 * hash + execution.getArtifactId().hashCode(); + hash = 17 * hash + execution.getVersion().hashCode(); + hash = 17 * execution.getGoal().hashCode(); + return hash; + } + + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(!(obj instanceof MojoExecutionKey)) { + return false; + } + + MojoExecutionKey other = (MojoExecutionKey) obj; + + return execution.getGroupId().equals(other.execution.getGroupId()) + && execution.getArtifactId().equals(other.execution.getArtifactId()) + && execution.getVersion().equals(other.execution.getVersion()) + && execution.getGoal().equals(other.execution.getGoal()); + } + } + + public List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade facade, IProgressMonitor monitor) + throws CoreException { + + List<AbstractProjectConfigurator> configurators = super.getProjectConfigurators(facade, monitor); + + Map<MojoExecutionKey, AbstractProjectConfigurator> executions = new LinkedHashMap<MojoExecutionKey, AbstractProjectConfigurator>(); + + MavenExecutionPlan executionPlan = facade.getExecutionPlan(monitor); + execution: for(MojoExecution execution : executionPlan.getMojoExecutions()) { + MojoExecutionKey key = new MojoExecutionKey(execution); + for(AbstractProjectConfigurator configurator : configurators) { + if(configurator.isSupportedExecution(execution)) { + executions.put(key, configurator); + continue execution; + } + } + executions.put(key, null); + } + + for(Map.Entry<MojoExecutionKey, AbstractProjectConfigurator> entry : executions.entrySet()) { + MojoExecutionKey key = entry.getKey(); + if(entry.getValue() == null) { + // make sure to reuse the same instance of project configurator + for(AbstractProjectConfigurator configurator : executions.values()) { + if(configurator != null && configurator.isSupportedExecution(key.getMojoExecution())) { + entry.setValue(configurator); + break; + } + } + } + if(entry.getValue() == null) { + AbstractProjectConfigurator configurator = LifecycleMappingFactory.createProjectConfiguratorFor(key + .getMojoExecution()); + if(configurator != null) { + entry.setValue(configurator); + } + } + } + + ArrayList<AbstractProjectConfigurator> result = new ArrayList<AbstractProjectConfigurator>(); + + for(AbstractProjectConfigurator configurator : executions.values()) { + if(configurator != null && !result.contains(configurator)) { + result.add(configurator); + } + } + + return result; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ILifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ILifecycleMapping.java new file mode 100644 index 00000000..80ece0df --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ILifecycleMapping.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; + +/** + * LifecycleMapping + * + * @author igor + */ +public interface ILifecycleMapping { + String getId(); + + String getName(); + + /** + * Configure Eclipse workspace project according to Maven build project configuration. + */ + void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException; + + /** + * Undo any Eclipse project configuration done during previous call(s) to {@link #configure(ProjectConfigurationRequest, IProgressMonitor)} + */ + void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException; + + /** + * Returns list of AbstractBuildParticipant that need to be executed during + * Eclipse workspace build. List can be empty but cannot be null. + */ + List<AbstractBuildParticipant> getBuildParticipants(IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException; + + /** TODO does this belong here? */ + List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade facade, IProgressMonitor monitor) throws CoreException; + + List<MojoExecution> getNotCoveredMojoExecutions(IMavenProjectFacade mavenProjectFacade, IProgressMonitor monitor) + throws CoreException; + + boolean isInterestingPhase(String phase); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenProjectConfigurator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenProjectConfigurator.java new file mode 100644 index 00000000..204fcb98 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenProjectConfigurator.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.model.Build; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.project.ResolverConfiguration; + + +/** + * Generic project configurator that using Maven plugins + * + * @author Eugene Kuleshov + * @see AbstractProjectConfigurator + */ +public class MavenProjectConfigurator extends AbstractProjectConfigurator { + + String pluginKey; + + List<String> goals; + + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) { + if(pluginKey == null || goals == null) { + return; + } + + MavenProject mavenProject = request.getMavenProject(); + Build build = mavenProject.getBuild(); + if(build != null) { + Map<String, Plugin> pluginMap = build.getPluginsAsMap(); + Plugin mavenPlugin = pluginMap.get(pluginKey); + if(mavenPlugin != null) { + IFile pomFile = request.getPom(); + ResolverConfiguration resolverConfiguration = request.getResolverConfiguration(); + // MavenPlugin plugin = MavenPlugin.getDefault(); + try { + IMaven maven = MavenPlugin.getDefault().getMaven(); + MavenExecutionRequest executionRequest = projectManager.createExecutionRequest(pomFile, resolverConfiguration, monitor); + executionRequest.setGoals(goals); + maven.execute(executionRequest, monitor); + } catch(Exception ex) { + String msg = ex.getMessage() == null ? ex.toString() : ex.getMessage(); + console.logError(msg); + MavenLogger.log(msg, ex); + } + + try { + request.getProject().refreshLocal(IResource.DEPTH_INFINITE, monitor); + } catch(CoreException ex) { + IStatus status = ex.getStatus(); + String msg = status.getMessage(); + Throwable t = status.getException(); + console.logError(msg + (t == null ? "" : "; " + t.toString())); //$NON-NLS-1$ //$NON-NLS-2$ + MavenLogger.log(ex); + } + } + } + } + + public String getPluginKey() { + return this.pluginKey; + } + + public List<String> getGoals() { + return this.goals; + } + + // IExecutableExtension + + @Override + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + super.setInitializationData(config, propertyName, data); + + Pattern pattern = Pattern.compile("(.+?)\\:(.+?)\\|(.+)"); //$NON-NLS-1$ + String params = (String) data; + if(params != null) { + Matcher matcher = pattern.matcher(params); + if(matcher.find() && matcher.groupCount() == 3) { + pluginKey = matcher.group(1) + ":" + matcher.group(2); //$NON-NLS-1$ + goals = Arrays.asList(matcher.group(3).split("\\|")); //$NON-NLS-1$ + return; + } + } + MavenLogger.log("Unable to parse configuration for project configurator " + getId() + "; " + data, null); //$NON-NLS-2$ + } + + @Override + public String toString() { + return super.toString() + " " + pluginKey + goals; //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenResourcesProjectConfigurator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenResourcesProjectConfigurator.java new file mode 100644 index 00000000..87c013fe --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MavenResourcesProjectConfigurator.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import org.eclipse.m2e.core.internal.project.MojoExecutionProjectConfigurator; + +/** + * Project configurator for maven-resources-plugin + */ +public class MavenResourcesProjectConfigurator extends MojoExecutionProjectConfigurator { + public MavenResourcesProjectConfigurator() { + super(true /*runOnIncremental*/); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MojoExecutionBuildParticipant.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MojoExecutionBuildParticipant.java new file mode 100644 index 00000000..f0852f35 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/MojoExecutionBuildParticipant.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.MojoExecution; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.IMaven; + + +/** + * MojoExecutionBuildParticipant + * + * @author igor + */ +public class MojoExecutionBuildParticipant extends AbstractBuildParticipant { + + private final MojoExecution execution; + private final boolean runOnIncremental; + + public MojoExecutionBuildParticipant(MojoExecution execution, boolean runOnIncremental) { + this.execution = execution; + this.runOnIncremental = runOnIncremental; + } + + public Set<IProject> build(int kind, IProgressMonitor monitor) throws Exception { + if(appliesToBuildKind(kind)) { + IMaven maven = MavenPlugin.getDefault().getMaven(); + + maven.execute(getSession(), getMojoExecution(), monitor); + } + return null; + } + + public boolean appliesToBuildKind(int kind) { + if(IncrementalProjectBuilder.FULL_BUILD == kind || IncrementalProjectBuilder.CLEAN_BUILD == kind) { + return true; + } + return runOnIncremental; + } + + public MojoExecution getMojoExecution() { + return execution; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/NoopLifecycleMapping.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/NoopLifecycleMapping.java new file mode 100644 index 00000000..9273aa71 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/NoopLifecycleMapping.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; + +/** + * NoopLifecycleMapping + * + * @author igor + */ +public class NoopLifecycleMapping extends AbstractLifecycleMapping { + + public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + // do nothing + } + + public List<AbstractBuildParticipant> getBuildParticipants(IMavenProjectFacade facade, IProgressMonitor monitor) + throws CoreException { + return new ArrayList<AbstractBuildParticipant>(); + } + + public List<AbstractProjectConfigurator> getProjectConfigurators(IMavenProjectFacade facade, IProgressMonitor monitor) + throws CoreException { + return new ArrayList<AbstractProjectConfigurator>(); + } + + public void unconfigure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException { + // do nothing + } + + public List<String> getPotentialMojoExecutionsForBuildKind(IMavenProjectFacade facade, int kind, IProgressMonitor progressMonitor) { + return Collections.emptyList(); + } + + public boolean isInterestingPhase(String phase) { + return false; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/PluginExecutionFilter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/PluginExecutionFilter.java new file mode 100644 index 00000000..18f8b260 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/PluginExecutionFilter.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.plugin.MojoExecution; + + +public final class PluginExecutionFilter { + + private final String groupId; + + private final String artifactId; + + private final String versionRange; + + private final Set<String> goals; + + private final VersionRange parsedVersionRange; + + public PluginExecutionFilter(String groupId, String artifactId, String versionRange, String goals) { + this.groupId = groupId; + this.artifactId = artifactId; + this.versionRange = versionRange; + this.goals = new LinkedHashSet<String>(Arrays.asList(goals.split(","))); //$NON-NLS-1$; + try { + this.parsedVersionRange = VersionRange.createFromVersionSpec(versionRange); + } catch(InvalidVersionSpecificationException e) { + throw new IllegalArgumentException("Can't parse version range", e); + } + } + + public String getVersionRange() { + return this.versionRange; + } + + public String getGroupId() { + return this.groupId; + } + + public String getArtifactId() { + return this.artifactId; + } + + public Set<String> getGoals() { + return this.goals; + } + + /** + * @return true if mojoExecution matches this key or false otherwise + */ + public boolean match(MojoExecution mojoExecution) { + if(!groupId.equals(mojoExecution.getGroupId()) || !artifactId.equals(mojoExecution.getArtifactId())) { + return false; + } + + DefaultArtifactVersion version = new DefaultArtifactVersion(mojoExecution.getVersion()); + + if(!parsedVersionRange.containsVersion(version)) { + return false; + } + + return goals.contains(mojoExecution.getGoal()); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ProjectConfigurationRequest.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ProjectConfigurationRequest.java new file mode 100644 index 00000000..f6f08f49 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/configurator/ProjectConfigurationRequest.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.project.configurator; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; + +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.ResolverConfiguration; + +/** + * ProjectConfigurationRequest + * + * @author igor + */ +public class ProjectConfigurationRequest { + private final boolean updateSources; + private final IMavenProjectFacade facade; + private final MavenProject mavenProject; + private final MavenSession mavenSession; + + public ProjectConfigurationRequest(IMavenProjectFacade facade, MavenProject mavenProject, MavenSession mavenSession, boolean updateSources) { + this.facade = facade; + this.mavenSession = mavenSession; + this.updateSources = updateSources; + this.mavenProject = mavenProject; + } + + public IProject getProject() { + return facade.getProject(); + } + + public ResolverConfiguration getResolverConfiguration() { + return facade.getResolverConfiguration(); + } + + public boolean isProjectConfigure() { + return updateSources; + } + + public boolean isProjectImport() { + return !updateSources; + } + + public MavenProject getMavenProject() { + return mavenProject; + } + + public MavenSession getMavenSession() { + return mavenSession; + } + + public IFile getPom() { + return facade.getPom(); + } + + public IMavenProjectFacade getMavenProjectFacade() { + return facade; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepository.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepository.java new file mode 100644 index 00000000..285cc99e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepository.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.repository; + +import java.io.File; + +import org.apache.maven.wagon.authentication.AuthenticationInfo; + +/** + * Repository tracked by repository registry. + * + * @author igor + */ +public interface IRepository { + + /* + * Element/attribute names in settings.xml are quite confusing. + * + * "server" defines repository access credentials. + * + * "mirror/id" references server/id, i.e. credentials used to + * access the mirror. It does NOT identify the mirror. + * + * "repository/id" references server/id but also used to override repository + * definition. For example, repository with id=central defined in settings.xml + * overrides definition of central hardcoded in maven code. + * + */ + + /** + * Repository access credentials. Can be null. + */ + public AuthenticationInfo getAuthenticationInfo(); + + /** + * Repository URL + */ + public String getUrl(); + + /** + * For local repositories, returns basedir of repository contents. + * + * Returns null for remote repositories; + */ + public File getBasedir(); + + /** + * Repository id element as defined in settings.xml or pom.xml file. + * + * Note that repository id is a reference to server element in settings.xml file, + * it does not uniquely identify a repository. + */ + public String getId(); + + /** + * Unique repository id. Generated based on combination of repository url and userId. + * Can be used to store repository-related information on local filesystem. + */ + public String getUid(); + + /** + * Indicates that repository id matches mirrorOf clause of a mirror. In other + * words, all repository requests will be redirected to a mirror. + * + * If null, repository is accessed directly. + * + * TODO decide return value format. + */ + public String getMirrorId(); + + /** + * For repository mirrors, returns value of mirrorOf element as defined in settings.xml. + * + * Returns null for other repositories. + */ + public String getMirrorOf(); + + /** + * Protocol part of repository url, i.e. "file", "http", etc. + */ + public String getProtocol(); + + public boolean isScope(int scope); + + /** + * Human readable repository identifier + */ + public String toString(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepositoryRegistry.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepositoryRegistry.java new file mode 100644 index 00000000..5e676adf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/repository/IRepositoryRegistry.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.repository; + +import java.util.List; + +import org.eclipse.m2e.core.embedder.ArtifactRepositoryRef; + +/** + * Registry of repositories accessible by m2e. + * + * The registry automatically tracks the following repositories + * + * <dl> + * <li>Maven local repository + * <li>Workspace repository + * <li>Mirrors defined in settings.xml + * <li>Repositories and pluginRepositories defined in active profiles in + * settings.xml + * <li>Repositories and pluginRepositories defined in pom.xml files of + * workspace Maven projects. + * </dl> + * + * @author igor + */ +public interface IRepositoryRegistry { + + /** + * + */ + public static final int SCOPE_UNKNOWN = 1; + + /** + * Maven local repositories. + */ + public static final int SCOPE_LOCAL = 1 << 1; + + /** + * Eclipse workspace repository + */ + public static final int SCOPE_WORKSPACE = 1 << 2; + + /** + * Repositories defined in settings.xml file. + */ + public static final int SCOPE_SETTINGS = 1 << 3; + + /** + * Repositories defined in pom.xml files of workspace Maven projects + */ + public static final int SCOPE_PROJECT = 1 << 4; + + + public List<IRepository> getRepositories(int scope); + + public IRepository getWorkspaceRepository(); + + public IRepository getLocalRepository(); + + public IRepository getRepository(ArtifactRepositoryRef repositoryRef); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/MavenCheckoutOperation.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/MavenCheckoutOperation.java new file mode 100644 index 00000000..abdabde3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/MavenCheckoutOperation.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.MavenProjectScmInfo; + + +/** + * Checkout operation + * + * @author Eugene Kuleshov + */ +public class MavenCheckoutOperation { + + private final MavenConsole console; + + private final File location; + + private final Collection<MavenProjectScmInfo> mavenProjects; + + private final List<String> locations = new ArrayList<String>(); + + public MavenCheckoutOperation(File location, Collection<MavenProjectScmInfo> mavenProjects, MavenConsole console) { + this.location = location; + this.mavenProjects = mavenProjects; + this.console = console; + } + + public void run(IProgressMonitor monitor) throws InterruptedException, CoreException { + List<MavenProjectScmInfo> flatProjects = new ArrayList<MavenProjectScmInfo>(); + + // sort nested projects + for(MavenProjectScmInfo info : mavenProjects) { + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + String folderUrl = info.getFolderUrl(); + + monitor.setTaskName(NLS.bind(Messages.MavenCheckoutOperation_task_scanning, info.getLabel(), info.getFolderUrl())); + + // XXX check if projects already exist + boolean isNestedPath = false; + for(MavenProjectScmInfo info2 : mavenProjects) { + if(info != info2) { + String path = info2.getFolderUrl(); + if(folderUrl.startsWith(path + "/")) { //$NON-NLS-1$ + isNestedPath = true; + break; + } + } + } + if(!isNestedPath) { + flatProjects.add(info); + } + } + + for(MavenProjectScmInfo info : flatProjects) { + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + monitor.setTaskName(NLS.bind(Messages.MavenCheckoutOperation_task_checking,info.getLabel(), info.getFolderUrl())); + + // XXX if location is pointing to workspace folder need to create unique dir too + File workspaceRoot = ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile(); + File location = getUniqueDir(this.location == null ? workspaceRoot : this.location); + + ScmHandler handler = ScmHandlerFactory.getHandler(info.getFolderUrl()); + if(handler == null) { + String msg = "SCM provider is not available for " + info.getFolderUrl(); + console.logError(msg); + } else { + handler.checkoutProject(info, location, monitor); + locations.add(location.getAbsolutePath()); + } + + } + + } + + protected File getUniqueDir(File baseDir) { + long suffix = System.currentTimeMillis(); + while(true) { + File tempDir = new File(baseDir, "maven." + suffix); //$NON-NLS-1$ + if(!tempDir.exists()) { + return tempDir; + } + suffix++; + } + } + + /** + * @return Returns collection of {@link MavenProjectScmInfo} + */ + public Collection<MavenProjectScmInfo> getMavenProjects() { + return this.mavenProjects; + } + + /** + * @return Returns list of <code>String</code> paths for the checked out locations + */ + public List<String> getLocations() { + return this.locations; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandler.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandler.java new file mode 100644 index 00000000..9adb53a3 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandler.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +import java.io.File; +import java.io.InputStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.MavenProjectScmInfo; + +/** + * An SCM handler base class + * + * @author Eugene Kuleshov + */ +public abstract class ScmHandler implements Comparable<ScmHandler>, IExecutableExtension { + + public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + public static final String ATTR_TYPE = "type"; //$NON-NLS-1$ + public static final String ATTR_PRIORITY = "priority"; //$NON-NLS-1$ + + private String type; + private int priority; + + public String getType() { + return type; + } + + public int getPriority() { + return priority; + } + + /** + * Opens resource from SCM + * + * @param url an url in maven-scm format for the resource to open + * @param revision a resource revision to open + * + * @throws CoreException when selected resource can't be open + * + * @see http://maven.apache.org/scm/scm-url-format.html + */ + public InputStream open(String url, String revision) throws CoreException { + return null; + } + + /** + * @param info + * @param location + * @param monitor + */ + public abstract void checkoutProject(MavenProjectScmInfo info, // + File location, IProgressMonitor monitor) throws CoreException, InterruptedException; + + // IExecutableExtension + + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + String handlerClass = config.getAttribute(propertyName); + String type = config.getAttribute(ATTR_TYPE); + String priority = config.getAttribute(ATTR_PRIORITY); + + this.type = type; + + if(priority!=null) { + try { + this.priority = Integer.parseInt(priority); + } catch(Exception ex) { + MavenLogger.log("Unable to parse priority for " + handlerClass, ex); + } + } + } + + // Comparable + + public int compareTo(ScmHandler o) { + if(o != null) { + ScmHandler handler = o; + int res = getType().compareTo(handler.getType()); + if(res==0) { + res = getPriority() - handler.getPriority(); + } + return res; + } + return -1; + } + + public int hashCode() { + final int prime = 31; + int result = prime + this.priority; + return prime * result + ((this.type == null) ? 0 : this.type.hashCode()); + } + + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj == null) { + return false; + } + if(getClass() != obj.getClass()) { + return false; + } + ScmHandler other = (ScmHandler) obj; + if(this.priority != other.priority) { + return false; + } + if(this.type == null) { + if(other.type != null) { + return false; + } + } else if(!this.type.equals(other.type)) { + return false; + } + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerFactory.java new file mode 100644 index 00000000..8d3c7d92 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerFactory.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * An SCM handler factory + * + * @author Eugene Kuleshov + */ +public class ScmHandlerFactory { + + + public static final String EXTENSION_SCM_HANDLERS = IMavenConstants.PLUGIN_ID + ".scmHandlers"; //$NON-NLS-1$ + + public static final String EXTENSION_SCM_HANDLERS_UI = IMavenConstants.PLUGIN_ID + ".scmHandlersUi"; //$NON-NLS-1$ + + private static final String ELEMENT_SCM_HANDLER = "handler"; //$NON-NLS-1$ + + private static final String ELEMENT_SCM_HANDLER_UI = "handlerUi"; //$NON-NLS-1$ + + private static volatile Map<String, List<ScmHandler>> scms; + + private static volatile Map<String, ScmHandlerUi> scmUis; + + public static synchronized void addScmHandlerUi(ScmHandlerUi handlerUi) { + getScmUis().put(handlerUi.getType(), handlerUi); + } + + public static synchronized ScmHandlerUi getHandlerUiByType(String type) { + return type == null ? null : getScmUis().get(type); + } + + public static synchronized void addScmHandler(ScmHandler handler) { + List<ScmHandler> handlers = getScms().get(handler.getType()); + if(handlers == null) { + handlers = new ArrayList<ScmHandler>(); + getScms().put(handler.getType(), handlers); + } + handlers.add(handler); + Collections.sort(handlers); + } + + public static synchronized String[] getTypes() { + Map<String, List<ScmHandler>> scms = getScms(); + return scms.keySet().toArray(new String[scms.size()]); + } + + public static synchronized ScmHandler getHandler(String url) throws CoreException { + String type = ScmUrl.getType(url); + return getHandlerByType(type); + } + + public static synchronized ScmHandler getHandlerByType(String type) { + List<ScmHandler> handlers = getScms().get(type); + if(handlers == null) { + return null; + } + return handlers.get(0); + } + + private static Map<String, List<ScmHandler>> getScms() { + if(scms == null) { + scms = new TreeMap<String, List<ScmHandler>>(); + for(ScmHandler scmHandler : readScmHanderExtensions()) { + addScmHandler(scmHandler); + } + } + return scms; + } + + private static Map<String, ScmHandlerUi> getScmUis() { + if(scmUis == null) { + scmUis = new TreeMap<String, ScmHandlerUi>(); + List<ScmHandlerUi> scmHandlerUis = readScmHandlerUiExtensions(); + for(ScmHandlerUi scmHandlerUi : scmHandlerUis) { + addScmHandlerUi(scmHandlerUi); + } + } + return scmUis; + } + + private static List<ScmHandler> readScmHanderExtensions() { + List<ScmHandler> scmHandlers = new ArrayList<ScmHandler>(); + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint scmHandlersExtensionPoint = registry.getExtensionPoint(EXTENSION_SCM_HANDLERS); + if(scmHandlersExtensionPoint != null) { + IExtension[] scmHandlersExtensions = scmHandlersExtensionPoint.getExtensions(); + for(IExtension extension : scmHandlersExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_SCM_HANDLER)) { + try { + scmHandlers.add((ScmHandler) element.createExecutableExtension(ScmHandler.ATTR_CLASS)); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + return scmHandlers; + } + + private static List<ScmHandlerUi> readScmHandlerUiExtensions() { + ArrayList<ScmHandlerUi> scmHandlerUis = new ArrayList<ScmHandlerUi>(); + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint scmHandlersUiExtensionPoint = registry.getExtensionPoint(EXTENSION_SCM_HANDLERS_UI); + if(scmHandlersUiExtensionPoint != null) { + IExtension[] scmHandlersUiExtensions = scmHandlersUiExtensionPoint.getExtensions(); + for(IExtension extension : scmHandlersUiExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_SCM_HANDLER_UI)) { + try { + scmHandlerUis.add((ScmHandlerUi) element.createExecutableExtension(ScmHandlerUi.ATTR_CLASS)); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + return scmHandlerUis; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerUi.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerUi.java new file mode 100644 index 00000000..bb8d4ec8 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmHandlerUi.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.swt.widgets.Shell; + +/** + * An SCM handler UI base class + * + * @author Eugene Kuleshov + */ +public abstract class ScmHandlerUi implements IExecutableExtension { + public static final String ATTR_TYPE = "type"; //$NON-NLS-1$ + public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + + private String type; + + public String getType() { + return type; + } + + /** + * Show revision/tag browser dialog and allow user to select revision/tag + * + * @param shell the shell for revison/tag browser dialog + * @param scmUrl the current <code>ScmUrl</code>, or null if none + * @param scmRevision the current revision, or null if none + * @return String selected revision + */ + public String selectRevision(Shell shell, ScmUrl scmUrl, String scmRevision) { + return null; + } + + /** + * Show repository browser dialog and allow user to select location + * + * @param shell the shell for repository browser dialog + * @param scmUrl the current <code>ScmUrl</code>, or null if none + * @return ScmUrl for selected location or null if dialog was canceled + */ + public ScmUrl selectUrl(Shell shell, ScmUrl scmUrl) { + return null; + } + + public boolean isValidUrl(String scmUrl) { + return false; + } + + public boolean isValidRevision(ScmUrl scmUrl, String scmRevision) { + return false; + } + + public boolean canSelectUrl() { + return false; + } + + public boolean canSelectRevision() { + return false; + } + + + // IExecutableExtension + + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + this.type = config.getAttribute(ATTR_TYPE); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmTag.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmTag.java new file mode 100644 index 00000000..867357ca --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmTag.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +/** + * An SCM wrapper for tags + * + * @author Eugene Kuleshov + */ +public class ScmTag { + + public static enum Type { + HEAD, TAG, BRANCH, DATE; + } + + private final String name; + private final Type type; + + + public ScmTag(String name) { + this(name, null); + } + + public ScmTag(String name, Type type) { + this.name = name; + this.type = type; + } + + /** + * Returns tag name + */ + public String getName() { + return this.name; + } + + /** + * Returns tag type + */ + public Type getType() { + return this.type; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmUrl.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmUrl.java new file mode 100644 index 00000000..b3eb3ecf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/scm/ScmUrl.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.scm; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.internal.Messages; + +/** + * An SCM URL wrapper used to adapt 3rd party resources: + * + * <pre> + * scm:{scm_provider}:{scm_provider_specific_part} + * </pre> + * + * @see http://maven.apache.org/scm/scm-url-format.html + * @see org.eclipse.core.runtime.IAdapterManager + * + * @author Eugene Kuleshov + */ +public class ScmUrl { + private final String scmUrl; + private final String scmParentUrl; + private final ScmTag tag; + + public ScmUrl(String scmUrl) { + this(scmUrl, null); + } + + public ScmUrl(String scmUrl, String scmParentUrl) { + this(scmUrl, null, null); + } + + public ScmUrl(String scmUrl, String scmParentUrl, ScmTag tag) { + this.scmUrl = scmUrl; + this.scmParentUrl = scmParentUrl; + this.tag = tag; + } + + /** + * Return SCM url + */ + public String getUrl() { + return scmUrl; + } + + /** + * Return SCM url for the parent folder + */ + public String getParentUrl() { + return scmParentUrl; + } + + /** + * Return SCM tag + */ + public ScmTag getTag() { + return this.tag; + } + + /** + * Return provider-specific part of the SCM url + * + * @return + */ + public String getProviderUrl() { + try { + String type = ScmUrl.getType(scmUrl); + return scmUrl.substring(type.length() + 5); + + } catch(CoreException ex) { + return null; + } + } + + public static synchronized String getType(String url) throws CoreException { + if(!url.startsWith("scm:")) { //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, NLS.bind(Messages.ScmUrl_error, url), null)); + } + int n = url.indexOf(":", 4); //$NON-NLS-1$ + if(n == -1) { + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, NLS.bind(Messages.ScmUrl_error, url), null)); + } + return url.substring(4, n); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AbstractMavenDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AbstractMavenDialog.java new file mode 100644 index 00000000..37a8d4ad --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AbstractMavenDialog.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.SelectionStatusDialog; + +import org.eclipse.m2e.core.MavenPlugin; + + +/** + * A dialog superclass, featuring position and size settings. + */ +public abstract class AbstractMavenDialog extends SelectionStatusDialog { + + protected static final String KEY_WIDTH = "width"; //$NON-NLS-1$ + protected static final String KEY_HEIGHT = "height"; //$NON-NLS-1$ + private static final String KEY_X = "x"; //$NON-NLS-1$ + private static final String KEY_Y = "y"; //$NON-NLS-1$ + protected IDialogSettings settings; + private Point location; + private Point size; + + /** + * @param parent + */ + protected AbstractMavenDialog(Shell parent, String settingsSection) { + super(parent); + + IDialogSettings pluginSettings = MavenPlugin.getDefault().getDialogSettings(); + IDialogSettings settings = pluginSettings.getSection(settingsSection); + if(settings == null) { + settings = new DialogSettings(settingsSection); + settings.put(KEY_WIDTH, 480); + settings.put(KEY_HEIGHT, 450); + pluginSettings.addSection(settings); + } + this.settings = settings; + } + + protected Point getInitialSize() { + Point result = super.getInitialSize(); + if(size != null) { + result.x = Math.max(result.x, size.x); + result.y = Math.max(result.y, size.y); + Rectangle display = getShell().getDisplay().getClientArea(); + result.x = Math.min(result.x, display.width); + result.y = Math.min(result.y, display.height); + } + return result; + } + + protected Point getInitialLocation(Point initialSize) { + Point result = super.getInitialLocation(initialSize); + if(location != null) { + result.x = location.x; + result.y = location.y; + Rectangle display = getShell().getDisplay().getClientArea(); + int xe = result.x + initialSize.x; + if(xe > display.width) { + result.x -= xe - display.width; + } + int ye = result.y + initialSize.y; + if(ye > display.height) { + result.y -= ye - display.height; + } + } + return result; + } + + public boolean close() { + writeSettings(); + return super.close(); + } + + /** + * Initializes itself from the dialog settings with the same state as at the + * previous invocation. + */ + protected void readSettings() { + try { + int x = settings.getInt(KEY_X); //$NON-NLS-1$ + int y = settings.getInt(KEY_Y); //$NON-NLS-1$ + location = new Point(x, y); + } catch(NumberFormatException e) { + location = null; + } + try { + int width = settings.getInt(KEY_WIDTH); //$NON-NLS-1$ + int height = settings.getInt(KEY_HEIGHT); //$NON-NLS-1$ + size = new Point(width, height); + + } catch(NumberFormatException e) { + size = null; + } + } + + /** + * Stores it current configuration in the dialog store. + */ + private void writeSettings() { + Point location = getShell().getLocation(); + settings.put(KEY_X, location.x); //$NON-NLS-1$ + settings.put(KEY_Y, location.y); //$NON-NLS-1$ + + Point size = getShell().getSize(); + settings.put(KEY_WIDTH, size.x); //$NON-NLS-1$ + settings.put(KEY_HEIGHT, size.y); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AddDependencyDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AddDependencyDialog.java new file mode 100644 index 00000000..3a19d329 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/AddDependencyDialog.java @@ -0,0 +1,617 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; + +import com.ibm.icu.text.DateFormat; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; + +import org.apache.lucene.search.BooleanQuery; + +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.graph.DependencyVisitor; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.UserInputSearchExpression; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.util.ProposalUtil; +import org.eclipse.m2e.core.util.search.Packaging; +import org.eclipse.m2e.core.wizards.MavenPomSelectionComponent; +import org.eclipse.m2e.core.wizards.WidthGroup; +import org.eclipse.m2e.model.edit.pom.Dependency; +import org.eclipse.m2e.model.edit.pom.PomFactory; + + +/** + * A Dialog whose primary goal is to allow the user to select a dependency, either by entering the GAV coordinates + * manually, or by search through a repository index. + * + * @author rgould + */ +public class AddDependencyDialog extends AbstractMavenDialog { + + public static final String[] SCOPES = new String[] {"compile", "provided", "runtime", "test", "system"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + /* + * dependencies under dependencyManagement are permitted to use an the extra "import" scope + */ + public static final String[] DEP_MANAGEMENT_SCOPES = new String[] {"compile", "provided", "runtime", "test", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "system", "import"}; //$NON-NLS-1$ //$NON-NLS-2$ + + protected static final String DIALOG_SETTINGS = AddDependencyDialog.class.getName(); + + protected static final long SEARCH_DELAY = 500L; //in milliseconds + + protected String[] scopes; + + protected TreeViewer resultsViewer; + + protected Text queryText; + + protected Text groupIDtext; + + protected Text artifactIDtext; + + protected Text versionText; + + protected Text infoTextarea; + + protected List scopeList; + + protected java.util.List<Dependency> dependencies; + + protected WidthGroup widthGroup; + + /* + * Stores selected files from the results viewer. These are later + * converted into the above dependencies when OK is pressed. + */ + protected java.util.List<IndexedArtifactFile> artifactFiles; + + protected SearchJob currentSearch; + + protected IProject project; + + protected DependencyNode dependencyNode; + + /* + * This is to be run when the dialog is done creating its controls, but + * before open() is called + */ + protected Runnable onLoad; + + protected SelectionListener resultsListener; + + /** + * The AddDependencyDialog differs slightly in behaviour depending on context. If it is being used to apply a + * dependency under the "dependencyManagement" context, the extra "import" scope is available. Set @param + * isForDependencyManagement to true if this is case. + * + * @param parent + * @param isForDependencyManagement + * @param project the project which contains this POM. Used for looking up indices + */ + public AddDependencyDialog(Shell parent, boolean isForDependencyManagement, IProject project) { + super(parent, DIALOG_SETTINGS); + this.project = project; + + setShellStyle(getShellStyle() | SWT.RESIZE); + setTitle(Messages.AddDependencyDialog_title); +// setStatusLineAboveButtons(true); + + if(!isForDependencyManagement) { + this.scopes = SCOPES; + } else { + this.scopes = DEP_MANAGEMENT_SCOPES; + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea() + */ + protected Control createDialogArea(Composite parent) { + readSettings(); + + Composite composite = (Composite) super.createDialogArea(parent); + + widthGroup = new WidthGroup(); + composite.addControlListener(widthGroup); + + Composite gavControls = createGAVControls(composite); + gavControls.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + + new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + + Composite searchControls = createSearchControls(composite); + searchControls.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Display.getDefault().asyncExec(this.onLoad); + + return composite; + } + + + /** + * Sets the up group-artifact-version controls + */ + private Composite createGAVControls(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + + GridLayout gridLayout = new GridLayout(4, false); + gridLayout.marginWidth = 0; + composite.setLayout(gridLayout); + + Label groupIDlabel = new Label(composite, SWT.NONE); + groupIDlabel.setText(Messages.AddDependencyDialog_groupId_label); + widthGroup.addControl(groupIDlabel); + + groupIDtext = new Text(composite, SWT.BORDER); + groupIDtext.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Label scopeLabel = new Label(composite, SWT.NONE); + scopeLabel.setText(Messages.AddDependencyDialog_scope_label); + + scopeList = new List(composite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL); + scopeList.setItems(scopes); + GridData scopeListData = new GridData(SWT.LEFT, SWT.FILL, false, true, 1, 3); + scopeListData.heightHint = 20; + scopeListData.widthHint = 100; + scopeList.setLayoutData(scopeListData); + scopeList.setSelection(0); + + Label artifactIDlabel = new Label(composite, SWT.NONE); + artifactIDlabel.setText(Messages.AddDependencyDialog_artifactId_label); + widthGroup.addControl(artifactIDlabel); + + artifactIDtext = new Text(composite, SWT.BORDER); + artifactIDtext.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Label filler = new Label(composite, SWT.NONE); + filler.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 2)); + + Label versionLabel = new Label(composite, SWT.NONE); + versionLabel.setText(Messages.AddDependencyDialog_version_label); + widthGroup.addControl(versionLabel); + + versionText = new Text(composite, SWT.BORDER); + versionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + /* + * Fix the tab order (group -> artifact -> version -> scope) + */ + composite.setTabList(new Control[] { groupIDtext, artifactIDtext, versionText, scopeList}); + + ProposalUtil.addGroupIdProposal(project, groupIDtext, Packaging.ALL); + ProposalUtil.addArtifactIdProposal(project, groupIDtext, artifactIDtext, Packaging.ALL); + ProposalUtil.addVersionProposal(project, groupIDtext, artifactIDtext, versionText, Packaging.ALL); + + artifactIDtext.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent e) { + updateInfo(); + } + }); + + groupIDtext.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent e) { + updateInfo(); + } + }); + + return composite; + } + + void updateInfo() { +// infoTextarea.setText(""); //$NON-NLS-1$ + if(dependencyNode == null) { + return; + } + dependencyNode.accept(new DependencyVisitor() { + + public boolean visitLeave(DependencyNode node) { + if(node.getDependency() != null && node.getDependency().getArtifact() != null) { + Artifact artifact = node.getDependency().getArtifact(); + if(artifact.getGroupId().equalsIgnoreCase(groupIDtext.getText().trim()) + && artifact.getArtifactId().equalsIgnoreCase(artifactIDtext.getText().trim())) { + infoTextarea.setText(NLS.bind(Messages.AddDependencyDialog_info_transitive, + new String[] {artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()})); + } + return false; + } + return true; + } + + public boolean visitEnter(DependencyNode node) { + return true; + } + }); + + } + + private Composite createSearchControls(Composite parent) { + SashForm sashForm = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH); + sashForm.setLayout(new FillLayout()); + + Composite resultsComposite = new Composite(sashForm, SWT.NONE); + GridLayout resultsLayout = new GridLayout(1, false); + resultsLayout.marginWidth = 0; + resultsComposite.setLayout(resultsLayout); + + Label queryLabel = new Label(resultsComposite, SWT.NONE); + queryLabel.setText(Messages.AddDependencyDialog_search_label); +// widthGroup.addControl(queryLabel); + + queryText = new Text(resultsComposite, SWT.BORDER | SWT.SEARCH); + queryText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + queryText.setFocus(); + +// queryText.setMessage(Messages.AddDependencyDialog_search_message); + + Label resultsLabel = new Label(resultsComposite, SWT.NONE); + resultsLabel.setText(Messages.AddDependencyDialog_results_label); + resultsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); +// widthGroup.addControl(resultsLabel); + + Tree resultsTree = new Tree(resultsComposite, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); + GridData treeData = new GridData(SWT.FILL, SWT.FILL, true, true); + treeData.heightHint = 140; + treeData.widthHint = 100; + resultsTree.setLayoutData(treeData); + + Composite infoComposite = new Composite(sashForm, SWT.NONE); + GridLayout infoLayout = new GridLayout(1, false); + infoLayout.marginWidth = 0; + infoComposite.setLayout(infoLayout); + + Label infoLabel = new Label(infoComposite, SWT.NONE); + infoLabel.setText(Messages.AddDependencyDialog_info_label); + infoLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); +// widthGroup.addControl(infoLabel); + + infoTextarea = new Text(infoComposite, SWT.MULTI | SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + GridData infoData = new GridData(SWT.FILL, SWT.FILL, true, true); + infoData.heightHint = 60; + infoData.widthHint = 100; + infoTextarea.setLayoutData(infoData); + + sashForm.setWeights(new int[] {70, 30}); + + /* + * Set up TreeViewer for search results + */ + + resultsViewer = new TreeViewer(resultsTree); + resultsViewer.setContentProvider(new MavenPomSelectionComponent.SearchResultContentProvider()); + //TODO we want to have the artifacts marked for presence and management.. + resultsViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new MavenPomSelectionComponent.SearchResultLabelProvider(Collections.EMPTY_SET, Collections.EMPTY_SET, + IIndex.SEARCH_ARTIFACT))); + + /* + * Hook up events + */ + + resultsListener = new SelectionListener(); + resultsViewer.addSelectionChangedListener(resultsListener); + + queryText.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if(e.keyCode == SWT.ARROW_DOWN) { + resultsViewer.getTree().setFocus(); + } + } + }); + + queryText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent e) { + search(queryText.getText()); + } + }); + + return sashForm; + } + + /** + * Just a short helper method to determine what to display in the text widgets when the user selects multiple objects + * in the tree viewer. If the objects have the same value, then we should show that to them, otherwise we show + * something like "(multiple selected)" + * + * @param current + * @param newValue + * @return + */ + String chooseWidgetText(String current, String newValue) { + if(current == null) { + return newValue; + } else if(!current.equals(newValue)) { + return Messages.AddDependencyDialog_multipleValuesSelected; + } + return current; + } + + void appendFileInfo(final StringBuffer buffer, final IndexedArtifactFile file) { + buffer.append(" * " + file.fname); + if(file.size != -1) { + buffer.append(", size: "); + if((file.size / 1024 / 1024) > 0) { + buffer.append((file.size / 1024 / 1024) + "MB"); + } else { + buffer.append(Math.max(1, file.size / 1024) + "KB"); + } + } + buffer.append(", date: " + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(file.date)); + buffer.append("\n"); + + if(dependencyNode != null) { + dependencyNode.accept(new DependencyVisitor() { + + public boolean visitEnter(DependencyNode node) { + return true; + } + + public boolean visitLeave(DependencyNode node) { + if(node.getDependency() == null || node.getDependency().getArtifact() == null) { + return true; + } + Artifact artifact = node.getDependency().getArtifact(); + if(artifact.getGroupId().equalsIgnoreCase(file.group) + && artifact.getArtifactId().equalsIgnoreCase(file.artifact)) { + buffer.append(NLS.bind(Messages.AddDependencyDialog_transitive_dependency, + new String[] {artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()})); + /* + * DependencyNodes don't know their parents. Determining which transitive dependency + * is using the selected dependency is non trivial :( + */ + return false; + } + return true; + } + }); + } + } + + protected void search(String query) { + if(query == null || query.length() <= 2) { + if(this.currentSearch != null) { + this.currentSearch.cancel(); + } + } else { + IndexManager indexManager = MavenPlugin.getDefault().getIndexManager(); + + if(this.currentSearch != null) { + this.currentSearch.cancel(); + } + + this.currentSearch = new SearchJob(query.toLowerCase(), indexManager); + this.currentSearch.schedule(SEARCH_DELAY); + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult() + * This is called when OK is pressed. There's no obligation to do anything. + */ + protected void computeResult() { + String scope = ""; //$NON-NLS-1$ + if(scopeList.getSelection().length != 0) { + scope = scopeList.getSelection()[0]; + } + + if(artifactFiles == null || artifactFiles.size() == 1) { + Dependency dependency = createDependency(groupIDtext.getText().trim(), artifactIDtext.getText().trim(), + versionText.getText().trim(), scope, ""); //$NON-NLS-1$ + this.dependencies = Collections.singletonList(dependency); + } else { + this.dependencies = new LinkedList<Dependency>(); + for(IndexedArtifactFile file : artifactFiles) { + Dependency dep = createDependency(file.group, file.artifact, file.version, scope, file.type); + this.dependencies.add(dep); + } + } + } + + private Dependency createDependency(String groupID, String artifactID, String version, String scope, String type) { + Dependency dependency = PomFactory.eINSTANCE.createDependency(); + dependency.setGroupId(groupID); + dependency.setArtifactId(artifactID); + dependency.setVersion(version); + + /* + * For scope and type, if the values are the default, don't save them. + * This reduces clutter in the XML file (although forces people who don't + * know what the defaults are to look them up). + */ + dependency.setScope("compile".equals(scope) ? "" : scope); //$NON-NLS-1$ //$NON-NLS-2$ + dependency.setType("jar".equals(type) ? "" : type); //$NON-NLS-1$ //$NON-NLS-2$ + + return dependency; + } + + public java.util.List<Dependency> getDependencies() { + return this.dependencies; + } + + void setInfo(int status, String message) { + updateStatus(new Status(status, IMavenConstants.PLUGIN_ID, message)); + } + + public final class SelectionListener implements ISelectionChangedListener { + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if(selection.isEmpty()) { + infoTextarea.setText(""); //$NON-NLS-1$ + artifactFiles = null; + } else { + String artifact = null; + String group = null; + String version = null; + + artifactFiles = new LinkedList<IndexedArtifactFile>(); + StringBuffer buffer = new StringBuffer(); + Iterator iter = selection.iterator(); + while(iter.hasNext()) { + Object obj = iter.next(); + IndexedArtifactFile file = null; + + if(obj instanceof IndexedArtifact) { + file = ((IndexedArtifact) obj).getFiles().iterator().next(); + } else { + file = (IndexedArtifactFile) obj; + } + + appendFileInfo(buffer, file); + artifactFiles.add(file); + + artifact = chooseWidgetText(artifact, file.artifact); + group = chooseWidgetText(group, file.group); + version = chooseWidgetText(version, file.version); + } + setInfo(OK, NLS.bind(artifactFiles.size() == 1 ? Messages.AddDependencyDialog_itemSelected : Messages.AddDependencyDialog_itemsSelected, artifactFiles.size())); + infoTextarea.setText(buffer.toString()); + artifactIDtext.setText(artifact); + groupIDtext.setText(group); + versionText.setText(version); + + boolean enabled = !(artifactFiles.size() > 1); + artifactIDtext.setEnabled(enabled); + groupIDtext.setEnabled(enabled); + versionText.setEnabled(enabled); + } + } + } + + private class SearchJob extends Job { + + private String query; + + private IndexManager indexManager; + + private boolean cancelled = false; + + public SearchJob(String query, IndexManager indexManager) { + super(NLS.bind(Messages.AddDependencyDialog_searchingFor, query)); + this.query = query; + this.indexManager = indexManager; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + if(this.cancelled || resultsViewer == null || resultsViewer.getControl() == null + || resultsViewer.getControl().isDisposed()) { + return Status.CANCEL_STATUS; + } + + try { + setResults(IStatus.OK, Messages.AddDependencyDialog_searching, Collections.<String, IndexedArtifact> emptyMap()); + // TODO: before it was searching all indexes, but it should current project? (cstamas) + // If not, the change getIndex(project) to getAllIndexes() and done + // TODO: cstamas identified this as "user input", true? + Map<String, IndexedArtifact> results = indexManager.getIndex(project).search(new UserInputSearchExpression(query), IIndex.SEARCH_ARTIFACT, + IIndex.SEARCH_ALL); + setResults(IStatus.OK, NLS.bind(Messages.AddDependencyDialog_searchDone, results.size()), results); + } catch(BooleanQuery.TooManyClauses exception) { + setResults(IStatus.ERROR, Messages.AddDependencyDialog_tooManyResults, + Collections.<String, IndexedArtifact> emptyMap()); + } catch(RuntimeException exception) { + setResults(IStatus.ERROR, NLS.bind(Messages.AddDependencyDialog_searchError, exception.toString()), + Collections.<String, IndexedArtifact> emptyMap()); + } catch(CoreException ex) { + setResults(IStatus.ERROR, NLS.bind(Messages.AddDependencyDialog_searchError, ex.getMessage()), + Collections.<String, IndexedArtifact> emptyMap()); + MavenLogger.log(ex); + } + + return Status.OK_STATUS; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#canceling() + */ + protected void canceling() { + this.cancelled = true; + super.canceling(); + } + + private void setResults(final int status, final String infoMessage, final Map<String, IndexedArtifact> results) { + if(cancelled) { + return; + } + + Display.getDefault().syncExec(new Runnable() { + + public void run() { + setInfo(status, infoMessage); + if(results != null && resultsViewer != null && resultsViewer.getControl() != null + && !resultsViewer.getControl().isDisposed()) { + resultsViewer.setInput(results); + } + } + }); + } + + } + + public void setDepdencyNode(DependencyNode node) { + this.dependencyNode = node; + } + + /** + * The provided runnable will be called after createDialogArea is done, but before it returns. This provides a way for + * long running operations to be executed in such a way as to not block the UI. This is primarily intended to allow + * the loading of the dependencyTree. The runnable should load the tree and then call setDependencyNode() + * + * @param runnable + */ + public void onLoad(Runnable runnable) { + this.onLoad = runnable; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/EditDependencyDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/EditDependencyDialog.java new file mode 100644 index 00000000..4baaaf4f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/EditDependencyDialog.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import static org.eclipse.m2e.core.util.Util.nvl; +import org.eclipse.core.resources.IProject; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.edit.command.SetCommand; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.util.M2EUtils; +import org.eclipse.m2e.core.util.ProposalUtil; +import org.eclipse.m2e.core.util.search.Packaging; +import org.eclipse.m2e.model.edit.pom.Dependency; +import org.eclipse.m2e.model.edit.pom.PomPackage; + + +public class EditDependencyDialog extends AbstractMavenDialog { + protected static PomPackage POM_PACKAGE = PomPackage.eINSTANCE; + + private static final String[] TYPES = new String[] {"jar", "war", "rar", "ear", "par", "ejb", "ejb-client", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ + "test-jar", "java-source", "javadoc", "maven-plugin", "pom"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + private EditingDomain editingDomain; + + private IProject project; + + private String[] scopes; + + protected Text groupIdText; + + protected Text artifactIdText; + + protected Text versionText; + + protected Text classifierText; + + protected Combo typeCombo; + + protected Combo scopeCombo; + + protected Text systemPathText; + +// protected Button selectSystemPathButton; + + protected Button optionalButton; + + private Dependency dependency; + + public EditDependencyDialog(Shell parent, boolean dependencyManagement, EditingDomain editingDomain, IProject project) { + super(parent, EditDependencyDialog.class.getName()); + this.editingDomain = editingDomain; + this.project = project; + + setShellStyle(getShellStyle() | SWT.RESIZE); + setTitle(Messages.EditDependencyDialog_title); + + if(!dependencyManagement) { + scopes = AddDependencyDialog.SCOPES; + } else { + scopes = AddDependencyDialog.DEP_MANAGEMENT_SCOPES; + } + } + + protected Control createDialogArea(Composite parent) { + readSettings(); + Composite superComposite = (Composite) super.createDialogArea(parent); + + Composite composite = new Composite(superComposite, SWT.NONE); + composite.setLayout(new GridLayout(3, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Label groupIdLabel = new Label(composite, SWT.NONE); + groupIdLabel.setText(Messages.EditDependencyDialog_groupId_label); + + groupIdText = new Text(composite, SWT.BORDER); + GridData gd_groupIdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); + gd_groupIdText.horizontalIndent = 4; + groupIdText.setLayoutData(gd_groupIdText); + ProposalUtil.addGroupIdProposal(project, groupIdText, Packaging.ALL); + M2EUtils.addRequiredDecoration(groupIdText); + + Label artifactIdLabel = new Label(composite, SWT.NONE); + artifactIdLabel.setText(Messages.EditDependencyDialog_artifactId_label); + + artifactIdText = new Text(composite, SWT.BORDER); + GridData gd_artifactIdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); + gd_artifactIdText.horizontalIndent = 4; + artifactIdText.setLayoutData(gd_artifactIdText); + ProposalUtil.addArtifactIdProposal(project, groupIdText, artifactIdText, Packaging.ALL); + M2EUtils.addRequiredDecoration(artifactIdText); + + Label versionLabel = new Label(composite, SWT.NONE); + versionLabel.setText(Messages.EditDependencyDialog_version_label); + + versionText = new Text(composite, SWT.BORDER); + GridData versionTextData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + versionTextData.horizontalIndent = 4; + versionTextData.widthHint = 200; + versionText.setLayoutData(versionTextData); + ProposalUtil.addVersionProposal(project, groupIdText, artifactIdText, versionText, Packaging.ALL); + + Label classifierLabel = new Label(composite, SWT.NONE); + classifierLabel.setText(Messages.EditDependencyDialog_classifier_label); + + classifierText = new Text(composite, SWT.BORDER); + GridData gd_classifierText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + gd_classifierText.horizontalIndent = 4; + gd_classifierText.widthHint = 200; + classifierText.setLayoutData(gd_classifierText); + ProposalUtil + .addClassifierProposal(project, groupIdText, artifactIdText, versionText, classifierText, Packaging.ALL); + + Label typeLabel = new Label(composite, SWT.NONE); + typeLabel.setText(Messages.EditDependencyDialog_type_label); + + typeCombo = new Combo(composite, SWT.NONE); + typeCombo.setItems(TYPES); + GridData gd_typeText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + gd_typeText.horizontalIndent = 4; + gd_typeText.widthHint = 120; + typeCombo.setLayoutData(gd_typeText); + + Label scopeLabel = new Label(composite, SWT.NONE); + scopeLabel.setText(Messages.EditDependencyDialog_scope_label); + + scopeCombo = new Combo(composite, SWT.NONE); + scopeCombo.setItems(scopes); + GridData gd_scopeText = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + gd_scopeText.horizontalIndent = 4; + gd_scopeText.widthHint = 120; + scopeCombo.setLayoutData(gd_scopeText); + + Label systemPathLabel = new Label(composite, SWT.NONE); + systemPathLabel.setText(Messages.EditDependencyDialog_systemPath_label); + + systemPathText = new Text(composite, SWT.BORDER); + GridData gd_systemPathText = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); + gd_systemPathText.horizontalIndent = 4; + gd_systemPathText.widthHint = 200; + systemPathText.setLayoutData(gd_systemPathText); + +// selectSystemPathButton = new Button(composite, SWT.NONE); +// selectSystemPathButton.setText("Select..."); + + new Label(composite, SWT.NONE); + + optionalButton = new Button(composite, SWT.CHECK); + optionalButton.setText(Messages.EditDependencyDialog_optional_checkbox); + GridData gd_optionalButton = new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1); + gd_optionalButton.horizontalIndent = 4; + optionalButton.setLayoutData(gd_optionalButton); + + composite.setTabList(new Control[] {groupIdText, artifactIdText, versionText, classifierText, typeCombo, + scopeCombo, systemPathText, /*selectSystemPathButton,*/optionalButton}); + + setDependency(dependency); + + return superComposite; + } + + protected void computeResult() { + CompoundCommand compoundCommand = new CompoundCommand(); + compoundCommand.append(createCommand(groupIdText.getText(), POM_PACKAGE.getDependency_GroupId(), "")); //$NON-NLS-1$ + compoundCommand.append(createCommand(artifactIdText.getText(), POM_PACKAGE.getDependency_ArtifactId(), "")); //$NON-NLS-1$ + compoundCommand.append(createCommand(versionText.getText(), POM_PACKAGE.getDependency_Version(), "")); //$NON-NLS-1$ + compoundCommand.append(createCommand(classifierText.getText(), POM_PACKAGE.getDependency_Classifier(), "")); //$NON-NLS-1$ + compoundCommand.append(createCommand(typeCombo.getText(), POM_PACKAGE.getDependency_Type(), "jar")); //$NON-NLS-1$ + compoundCommand.append(createCommand(scopeCombo.getText(), POM_PACKAGE.getDependency_Scope(), "compile")); //$NON-NLS-1$ + compoundCommand.append(createCommand(systemPathText.getText(), POM_PACKAGE.getDependency_SystemPath(), "")); //$NON-NLS-1$ + compoundCommand.append(createCommand(String.valueOf(optionalButton.getSelection()), + POM_PACKAGE.getDependency_Optional(), "false")); //$NON-NLS-1$ + editingDomain.getCommandStack().execute(compoundCommand); + } + + public void setDependency(Dependency dependency) { + this.dependency = dependency; + + if(dependency != null && groupIdText != null && !groupIdText.isDisposed()) { + groupIdText.setText(nvl(dependency.getGroupId())); + artifactIdText.setText(nvl(dependency.getArtifactId())); + versionText.setText(nvl(dependency.getVersion())); + classifierText.setText(nvl(dependency.getClassifier())); + typeCombo.setText("".equals(nvl(dependency.getType())) ? "jar" : dependency.getType()); //$NON-NLS-1$ //$NON-NLS-2$ + scopeCombo.setText("".equals(nvl(dependency.getScope())) ? "compile" : dependency.getScope()); //$NON-NLS-1$ //$NON-NLS-2$ + systemPathText.setText(nvl(dependency.getSystemPath())); + + boolean optional = Boolean.parseBoolean(dependency.getOptional()); + if(optionalButton.getSelection() != optional) { + optionalButton.setSelection(optional); + } + } + } + + private Command createCommand(String value, Object feature, String defaultValue) { + return SetCommand.create(editingDomain, dependency, feature, + value.length() == 0 || value.equals(defaultValue) ? SetCommand.UNSET_VALUE : value); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/InputHistory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/InputHistory.java new file mode 100644 index 00000000..7aca5f83 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/InputHistory.java @@ -0,0 +1,232 @@ + +package org.eclipse.m2e.core.ui.dialogs; + +import java.beans.Beans; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Control; + +import org.eclipse.m2e.core.MavenPlugin; + + +public class InputHistory { + /** the history limit */ + protected static final int MAX_HISTORY = 10; + + /** dialog settings to store input history */ + protected IDialogSettings dialogSettings; + + /** the Map of field ids to List of comboboxes that share the same history */ + private Map<String, List<ControlWrapper>> comboMap; + + private List<String> privileged; + + public InputHistory(String sectionName) { + this(sectionName, new String[0]); + } + + public InputHistory(String sectionName, String[] privileged) { + comboMap = new HashMap<String, List<ControlWrapper>>(); + + MavenPlugin plugin = MavenPlugin.getDefault(); + if(plugin != null) { + IDialogSettings pluginSettings = plugin.getDialogSettings(); + dialogSettings = pluginSettings.getSection(sectionName); + if(dialogSettings == null) { + dialogSettings = pluginSettings.addNewSection(sectionName); + pluginSettings.addSection(dialogSettings); + } + } + assert privileged != null; + this.privileged = Arrays.asList(privileged); + } + + /** Loads the input history from the dialog settings. */ + public void load() { + if(Beans.isDesignTime()) { + return; + } + + for(Map.Entry<String, List<ControlWrapper>> e : comboMap.entrySet()) { + String id = e.getKey(); + Set<String> items = new LinkedHashSet<String>(); + String[] itemsArr = dialogSettings.getArray(id); + items.addAll(privileged); + if(itemsArr != null) { + items.addAll(Arrays.asList(itemsArr)); + } + for(ControlWrapper wrapper : e.getValue()) { + if(!wrapper.isDisposed()) { + wrapper.setItems(items.toArray(new String[0])); + } + } + } + } + + /** Saves the input history into the dialog settings. */ + public void save() { + if(Beans.isDesignTime()) { + return; + } + + for(Map.Entry<String, List<ControlWrapper>> e : comboMap.entrySet()) { + String id = e.getKey(); + + Set<String> history = new LinkedHashSet<String>(MAX_HISTORY); + + for(ControlWrapper wrapper : e.getValue()) { + wrapper.collect(); + String lastValue = wrapper.text; + if(lastValue != null && lastValue.trim().length() > 0) { + history.add(lastValue); + } + } + + ControlWrapper wrapper = e.getValue().iterator().next(); + String[] items = wrapper.items; + if(items != null) { + for(int j = 0; j < items.length && history.size() < MAX_HISTORY; j++ ) { + // do not store the privileged items if they are not selected. + // we eventually inject the same or different set next time + if(!privileged.contains(items[j])) { + history.add(items[j]); + } + } + } + + dialogSettings.put(id, history.toArray(new String[history.size()])); + } + } + + /** Adds an input control to the list of fields to save. */ + public void add(Control combo) { + add(null, combo); + } + + /** Adds an input control to the list of fields to save. */ + public void add(String id, final Control combo) { + if(combo != null) { + if(id == null) { + id = String.valueOf(combo.getData("name")); + } + List<ControlWrapper> combos = comboMap.get(id); + if(combos == null) { + combos = new ArrayList<ControlWrapper>(); + comboMap.put(id, combos); + } + if(combo instanceof Combo) { + combos.add(new ComboWrapper((Combo) combo)); + } else if(combo instanceof CCombo) { + combos.add(new CComboWrapper((CCombo) combo)); + } + } + } + + abstract private class ControlWrapper { + protected Control control; + + protected String text; + + protected String[] items; + + private boolean collected; + + protected ControlWrapper(Control control) { + this.control = control; + control.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + collect(); + } + }); + } + + protected void collect() { + if(!collected && !isDisposed()) { + text = getText(); + items = getItems(); + } + collected = true; + } + + protected boolean isDisposed() { + return control.isDisposed(); + } + + abstract protected String getText(); + + abstract protected String[] getItems(); + + abstract protected void setItems(String[] items); + } + + private class ComboWrapper extends ControlWrapper { + private Combo combo; + + protected ComboWrapper(Combo combo) { + super(combo); + this.combo = combo; + } + + protected String getText() { + return combo.getText(); + } + + protected String[] getItems() { + return combo.getItems(); + } + + protected void setItems(String[] items) { + String value = combo.getText(); + combo.setItems(items); + if(value.length() > 0) { + // setItems() clears the text input, so we need to restore it + combo.setText(value); + } else if(items.length > 0) { + combo.setText(items[0]); + } + } + } + + private class CComboWrapper extends ControlWrapper { + private CCombo combo; + + protected CComboWrapper(CCombo combo) { + super(combo); + this.combo = combo; + } + + protected String getText() { + return combo.getText(); + } + + protected String[] getItems() { + try { + return combo.getItems(); + } catch(SWTException swtException) { + //CCombo throws this if the list is disposed, but the combo itself is not disposed yet + return new String[0]; + } + } + + protected void setItems(String[] items) { + String value = combo.getText(); + combo.setItems(items); + if(value.length() > 0) { + // setItems() clears the text input, so we need to restore it + combo.setText(value); + } + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenGoalSelectionDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenGoalSelectionDialog.java new file mode 100644 index 00000000..593d8feb --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenGoalSelectionDialog.java @@ -0,0 +1,359 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; +import org.eclipse.ui.dialogs.ISelectionStatusValidator; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; + + +public class MavenGoalSelectionDialog extends ElementTreeSelectionDialog { + + Button isQualifiedNameButton; + + boolean isQualifiedName = true; + + public MavenGoalSelectionDialog(Shell parent) { + super(parent, new GoalsLabelProvider(), new GoalsContentProvider()); + + setTitle(Messages.getString("launch.goalsDialog.title")); //$NON-NLS-1$ + setMessage(org.eclipse.m2e.core.internal.Messages.MavenGoalSelectionDialog_message); + setValidator(new GoalsSelectionValidator()); + setInput(new Object()); + } + + protected Control createDialogArea(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label selectGoalLabel = new Label(composite, SWT.NONE); + selectGoalLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenGoalSelectionDialog_lblSelect); + + final GoalsFilter filter = new GoalsFilter(); + + final Text filterText = new Text(composite, SWT.BORDER); + GridData gd_filterText = new GridData(SWT.FILL, SWT.CENTER, true, false); + gd_filterText.widthHint = 200; + filterText.setLayoutData(gd_filterText); + filterText.setFocus(); + + final TreeViewer treeViewer = createTreeViewer(composite); + treeViewer.addFilter(filter); + + GridData data = new GridData(GridData.FILL_BOTH); + data.widthHint = 500; + data.heightHint = 400; + // data.widthHint = convertWidthInCharsToPixels(fWidth); + // data.heightHint = convertHeightInCharsToPixels(fHeight); + + final Tree tree = treeViewer.getTree(); + tree.setLayoutData(data); + tree.setFont(parent.getFont()); + + filterText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + String text = filterText.getText(); + filter.setFilter(text); + treeViewer.refresh(); + if(text.trim().length() == 0) { + treeViewer.collapseAll(); + } else { + treeViewer.expandAll(); + } + } + }); + + filterText.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if(e.keyCode == SWT.ARROW_DOWN) { + tree.setFocus(); + tree.setSelection(tree.getTopItem().getItem(0)); + + Object[] elements = ((ITreeContentProvider) treeViewer.getContentProvider()).getElements(null); + treeViewer.setSelection(new StructuredSelection(elements[0])); + } + + } + }); + + isQualifiedNameButton = new Button(composite, SWT.CHECK); + isQualifiedNameButton.setText(org.eclipse.m2e.core.internal.Messages.MavenGoalSelectionDialog_btnQualified); + isQualifiedNameButton.setSelection(true); + isQualifiedNameButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + isQualifiedName = isQualifiedNameButton.getSelection(); + } + }); + +// if (fIsEmpty) { +// messageLabel.setEnabled(false); +// treeWidget.setEnabled(false); +// } + + return composite; + } + + public boolean isQualifiedName() { + return isQualifiedName; + } + + /** + * GoalsContentProvider + */ + static class GoalsContentProvider implements ITreeContentProvider { + private static Object[] EMPTY = new Object[0]; + + private final List<Group> groups = new ArrayList<Group>(); + + public GoalsContentProvider() { +// MavenEmbedderManager embedderManager = MavenPlugin.getDefault().getMavenEmbedderManager(); +// try { +// MavenEmbedder embedder = embedderManager.getWorkspaceEmbedder(); +// groups.add(new Group(Messages.getString("launch.goalsDialog.lifecycleBuild"), //$NON-NLS-1$ +// null, null, getLifecyclePhases(embedder.getBuildLifecyclePhases()))); +// groups.add(new Group(Messages.getString("launch.goalsDialog.lifecycleSite"), //$NON-NLS-1$ +// null, null, getLifecyclePhases(embedder.getSiteLifecyclePhases()))); +// groups.add(new Group(Messages.getString("launch.goalsDialog.lifecycleClean"), //$NON-NLS-1$ +// null, null, getLifecyclePhases(embedder.getCleanLifecyclePhases()))); +// } catch(Exception e) { +// MavenLogger.log("Unable to get lifecycle phases", e); +// } + + IndexManager indexManager = MavenPlugin.getDefault().getIndexManager(); + try { + // TODO: this will search ALL indexes, isn't the right to search _this_ project reposes only? + // I did not find (at first glance, maybe was hasty) a way to get IProject + Map<String, IndexedArtifact> result = indexManager.getAllIndexes().search(null, IIndex.SEARCH_PLUGIN); //$NON-NLS-1$ + TreeMap<String, Group> map = new TreeMap<String, Group>(); + for(IndexedArtifact a : result.values()) { + IndexedArtifactFile f = a.getFiles().iterator().next(); + if(f.prefix != null && f.prefix.length() > 0 && f.goals != null) { + List<Entry> goals = new ArrayList<Entry>(); + for(String goal : f.goals) { + if(goal.length() > 0) { + goals.add(new Entry(goal, f.prefix, f)); + } + } + if(goals.size() > 0) { + map.put(f.prefix + ":" + f.group, new Group(f.prefix, f.group, f.artifact, goals)); //$NON-NLS-1$ + } + } + } + groups.addAll(map.values()); + } catch(CoreException e) { + MavenLogger.log(e); + } + } + + private List<Entry> getLifecyclePhases(List<?> phases) { + List<Entry> entries = new ArrayList<Entry>(); + for(int i = 0; i < phases.size(); i++ ) { + entries.add(new Entry((String) phases.get(i), null, null)); + } + return entries; + } + + public Object[] getElements(Object inputElement) { + return groups.toArray(); + } + + public Object[] getChildren(Object parent) { + if(parent instanceof Group) { + return ((Group) parent).entries.toArray(); + } + return EMPTY; + } + + public boolean hasChildren(Object element) { + return element instanceof Group; + } + + public Object getParent(Object element) { + return null; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + + } + + /** + * GoalsLabelProvider + */ + static class GoalsLabelProvider extends LabelProvider { + public String getText(Object element) { + if(element instanceof Group) { + Group g = (Group) element; + if(g.groupId == null) { + return g.name; + } + return g.name + " - " + g.groupId + ":" + g.artifactId; //$NON-NLS-1$ //$NON-NLS-2$ + + } else if(element instanceof Entry) { + return ((Entry) element).name; + + } + return super.getText(element); + } + } + + /** + * GoalsFilter + */ + static class GoalsFilter extends ViewerFilter { + private String filter; + + public boolean select(Viewer viewer, Object parentElement, Object element) { + if(filter == null || filter.trim().length() == 0) { + return true; + } + if(element instanceof Group) { + Group g = (Group) element; + if(g.name.indexOf(filter) > -1) { + return true; + } + for(Iterator<Entry> it = g.entries.iterator(); it.hasNext();) { + Entry e = it.next(); + if(e.name.indexOf(filter) > -1) { + return true; + } + } + + } else if(element instanceof Entry) { + Entry e = (Entry) element; + return e.name.indexOf(filter) > -1 || (e.prefix != null && e.prefix.indexOf(filter) > -1); + + } + return false; + } + + public void setFilter(String filter) { + this.filter = filter; + } + } + + /** + * GoalsSelectionValidator + */ + static class GoalsSelectionValidator implements ISelectionStatusValidator { + public IStatus validate(Object[] selection) { + if(selection.length == 0) { + return new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + org.eclipse.m2e.core.internal.Messages.MavenGoalSelectionDialog_error, null); + } + for(int j = 0; j < selection.length; j++ ) { + if(selection[j] instanceof Entry) { + continue; + } + return new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, "", null); //$NON-NLS-1$ + } + return Status.OK_STATUS; + } + } + + /** + * Group + */ + static class Group { + public final String name; + + public final String groupId; + + public final String artifactId; + + public final List<Entry> entries; + + public Group(String name, String groupId, String artifactId, List<Entry> entries) { + this.name = name; + this.groupId = groupId; + this.artifactId = artifactId; + this.entries = entries; + } + } + + /** + * Entry + */ + public static class Entry { + public final String prefix; + + public final String name; + + private final IndexedArtifactFile f; + + public Entry(String name, String prefix, IndexedArtifactFile f) { + this.prefix = prefix; + this.name = name; + this.f = f; + } + + public String getName() { + return prefix == null ? name : prefix + ":" + name; //$NON-NLS-1$ + } + + public String getQualifiedName() { + // return prefix == null ? name : prefix + ":" + name; + return prefix == null ? name : f.group + ":" + f.artifact + ":" + f.version + ":" + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenMessageDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenMessageDialog.java new file mode 100644 index 00000000..ea439f57 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenMessageDialog.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +/** + * MavenMessageDialog + * + * @author dyocum + */ +public class MavenMessageDialog extends MessageDialog { + + private StyledText messageArea; + /** + * @param parentShell + * @param dialogTitle + * @param dialogTitleImage + * @param dialogMessage + * @param dialogImageType + * @param dialogButtonLabels + * @param defaultIndex + */ + public MavenMessageDialog(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, + int dialogImageType, String[] dialogButtonLabels, int defaultIndex) { + super(parentShell, dialogTitle, dialogTitleImage, dialogMessage, dialogImageType, dialogButtonLabels, defaultIndex); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.MessageDialog#createCustomArea(org.eclipse.swt.widgets.Composite) + */ + protected Control createCustomArea(Composite parent) { + // TODO Auto-generated method createCustomArea + this.messageArea = new StyledText(parent, SWT.WRAP|SWT.READ_ONLY|SWT.H_SCROLL|SWT.V_SCROLL|SWT.BORDER); + this.messageArea.setLayout(new GridLayout()); + GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true); + //size hints + gd.widthHint = 600; + gd.heightHint = 300; + messageArea.setLayoutData(gd); + return messageArea; + } + + /** + * + * @param parent Parent shell + * @param title Title of the dialog + * @param label The label shown above the msg. + * @param message The actual message to show in the text area. + */ + public static void openInfo(Shell parent, String title, String label, String message) { + MavenMessageDialog dialog = new MavenMessageDialog(parent, title, Dialog.getImage(Dialog.DLG_IMG_INFO), // accept + label, INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0); // ok + dialog.create(); + dialog.getMessageArea().setText(message); + dialog.getDialogArea().pack(true); + dialog.open(); + return; + } + + + /** + * @return Returns the messageArea. + */ + private StyledText getMessageArea() { + return messageArea; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenPropertyDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenPropertyDialog.java new file mode 100644 index 00000000..764714e6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenPropertyDialog.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +// import org.eclipse.debug.ui.StringVariableSelectionDialog; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.m2e.core.core.Messages; + + +public class MavenPropertyDialog extends Dialog { + + private final String title; + + private final String initialName; + + private final String initialValue; + + private final VerifyListener verifyListener; + + protected Text nameText; + + protected Text valueText; + + private String name; + + private String value; + + public MavenPropertyDialog(Shell shell, String title, String initialName, String initialValue, VerifyListener verifyListener) { + super(shell); + this.title = title; + this.initialName = initialName; + this.initialValue = initialValue; + this.verifyListener = verifyListener; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + protected Control createDialogArea(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(2, false); + gridLayout.marginTop = 7; + gridLayout.marginWidth = 12; + comp.setLayout(gridLayout); + + Label nameLabel = new Label(comp, SWT.NONE); + nameLabel.setText(Messages.getString("launch.propertyDialog.name")); //$NON-NLS-1$; + nameLabel.setFont(comp.getFont()); + + nameText = new Text(comp, SWT.BORDER | SWT.SINGLE); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.widthHint = 300; + nameText.setLayoutData(gd); + nameText.setFont(comp.getFont()); + nameText.setText(initialName==null ? "" : initialName); //$NON-NLS-1$ + nameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateButtons(); + } + }); + + Label valueLabel = new Label(comp, SWT.NONE); + valueLabel.setText(Messages.getString("launch.propertyDialog.value")); //$NON-NLS-1$; + valueLabel.setFont(comp.getFont()); + + valueText = new Text(comp, SWT.BORDER | SWT.SINGLE); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.widthHint = 300; + valueText.setLayoutData(gd); + valueText.setFont(comp.getFont()); + valueText.setText(initialValue==null ? "" : initialValue); //$NON-NLS-1$ + valueText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateButtons(); + } + }); + +// if(variables) { +// Button variablesButton = new Button(comp, SWT.PUSH); +// variablesButton.setText(Messages.getString("launch.propertyDialog.browseVariables")); //$NON-NLS-1$; +// gd = new GridData(GridData.HORIZONTAL_ALIGN_END); +// gd.horizontalSpan = 2; +// int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); +// gd.widthHint = Math.max(widthHint, variablesButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); +// variablesButton.setLayoutData(gd); +// variablesButton.setFont(comp.getFont()); +// +// variablesButton.addSelectionListener(new SelectionAdapter() { +// public void widgetSelected(SelectionEvent se) { +// StringVariableSelectionDialog variablesDialog = new StringVariableSelectionDialog(getShell()); +// if(variablesDialog.open() == IDialogConstants.OK_ID) { +// String variable = variablesDialog.getVariableExpression(); +// if(variable != null) { +// valueText.insert(variable.trim()); +// } +// } +// } +// }); +// } + + return comp; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) + */ + protected void buttonPressed(int buttonId) { + if(buttonId == IDialogConstants.OK_ID) { + name = nameText.getText(); + value = valueText.getText(); + } else { + name = null; + value = null; + } + super.buttonPressed(buttonId); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) + */ + protected void configureShell(Shell shell) { + super.configureShell(shell); + if(title != null) { + shell.setText(title); + } +// if (fInitialValues[0].length() == 0) { +// PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IAntUIHelpContextIds.ADD_PROPERTY_DIALOG); +// } else { +// PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IAntUIHelpContextIds.EDIT_PROPERTY_DIALOG); +// } + } + + /** + * Enable the OK button if valid input + */ + protected void updateButtons() { + String name = nameText.getText().trim(); + String value = valueText.getText().trim(); + // verify name + Event e = new Event(); + e.widget = nameText; + VerifyEvent ev = new VerifyEvent(e); + ev.doit = true; + if (verifyListener != null) { + ev.text = name; + verifyListener.verifyText(ev); + } + getButton(IDialogConstants.OK_ID).setEnabled((name.length() > 0) && (value.length() > 0) && ev.doit); + } + + /** + * Enable the buttons on creation. + * + * @see org.eclipse.jface.window.Window#create() + */ + public void create() { + super.create(); + updateButtons(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenRepositorySearchDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenRepositorySearchDialog.java new file mode 100644 index 00000000..206caf68 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/dialogs/MavenRepositorySearchDialog.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.dialogs; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.wizards.MavenPomSelectionComponent; + + +/** + * Maven POM Search dialog + * + * @author Eugene Kuleshov + */ +public class MavenRepositorySearchDialog extends AbstractMavenDialog { + private static final String DIALOG_SETTINGS = MavenRepositorySearchDialog.class.getName(); + + private final boolean showScope; + + private final Set<ArtifactKey> artifacts; + + private final Set<ArtifactKey> managed; + + /** + * One of + * {@link IIndex#SEARCH_ARTIFACT}, + * {@link IIndex#SEARCH_CLASS_NAME}, + */ + private final String queryType; + + private String queryText; + + MavenPomSelectionComponent pomSelectionComponent; + + private IndexedArtifact selectedIndexedArtifact; + + private IndexedArtifactFile selectedIndexedArtifactFile; + + private String selectedScope; + + private Combo scopeCombo; + + + /** + * Create repository search dialog + * + * @param parent parent shell + * @param title dialog title + * @param queryType one of + * {@link IIndex#SEARCH_ARTIFACT}, + * {@link IIndex#SEARCH_CLASS_NAME}, + * @param artifacts Set<Artifact> + * @deprecated + */ + public MavenRepositorySearchDialog(Shell parent, String title, String queryType, Set<ArtifactKey> artifacts) { + this(parent, title, queryType, artifacts, Collections.<ArtifactKey>emptySet(), false); + } + + public MavenRepositorySearchDialog(Shell parent, String title, String queryType, Set<ArtifactKey> artifacts, Set<ArtifactKey> managed) { + this(parent, title, queryType, artifacts, managed, false); + } + /** + * @deprecated + */ + public MavenRepositorySearchDialog(Shell parent, String title, String queryType, Set<ArtifactKey> artifacts, boolean showScope) { + this(parent, title, queryType, artifacts, Collections.<ArtifactKey>emptySet(), showScope); + } + + public MavenRepositorySearchDialog(Shell parent, String title, String queryType, Set<ArtifactKey> artifacts, Set<ArtifactKey> managed, boolean showScope) { + super(parent, DIALOG_SETTINGS); + this.artifacts = artifacts; + this.managed = managed; + this.queryType = queryType; + this.showScope = showScope; + + setShellStyle(getShellStyle() | SWT.RESIZE); + setStatusLineAboveButtons(true); + setTitle(title); + } + + public void setQuery(String query) { + this.queryText = query; + } + + protected Control createDialogArea(Composite parent) { + readSettings(); + + Composite composite = (Composite) super.createDialogArea(parent); + + pomSelectionComponent = new MavenPomSelectionComponent(composite, SWT.NONE); + pomSelectionComponent.init(queryText, queryType, artifacts, managed); + + pomSelectionComponent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + pomSelectionComponent.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + if (!pomSelectionComponent.getStatus().matches(IStatus.ERROR)) { + okPressedDelegate(); + } + } + }); + pomSelectionComponent.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateStatusDelegate(pomSelectionComponent.getStatus()); + } + }); + + return composite; + } + + protected void createButtonsForButtonBar(Composite parent) { + if(showScope) { + ((GridLayout) parent.getLayout()).numColumns += 2; + + Label scopeLabel = new Label(parent, SWT.NONE); + scopeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + scopeLabel.setText(Messages.MavenRepositorySearchDialog_lblScope); + + scopeCombo = new Combo(parent, SWT.BORDER | SWT.READ_ONLY); + scopeCombo.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); + scopeCombo.setItems(new String[] {"compile", "test", "runtime", "provided", "system", "import"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + scopeCombo.setText(Messages.MavenRepositorySearchDialog_7); + } + + super.createButtonsForButtonBar(parent); + } + + void okPressedDelegate() { + okPressed(); + } + + void updateStatusDelegate(IStatus status) { + updateStatus(status); + } + + protected void computeResult() { + selectedIndexedArtifact = pomSelectionComponent.getIndexedArtifact(); + selectedIndexedArtifactFile = pomSelectionComponent.getIndexedArtifactFile(); + selectedScope = scopeCombo == null ? null : scopeCombo.getText(); + setResult(Collections.singletonList(selectedIndexedArtifactFile)); + } + + public IndexedArtifact getSelectedIndexedArtifact() { + return this.selectedIndexedArtifact; + } + + public IndexedArtifactFile getSelectedIndexedArtifactFile() { + return this.selectedIndexedArtifactFile; + } + + public String getSelectedScope() { + return this.selectedScope; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenAdapterFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenAdapterFactory.java new file mode 100644 index 00000000..b09ccb05 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenAdapterFactory.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.ui.IActionFilter; + +/** + * @author Eugene Kuleshov + */ +@SuppressWarnings("unchecked") +public class MavenAdapterFactory implements IAdapterFactory { + + private static final Class[] ADAPTER_TYPES = new Class[] { IActionFilter.class }; + + public Class[] getAdapterList() { + return ADAPTER_TYPES; + } + + public Object getAdapter(final Object adaptable, Class adapterType) { + return new IActionFilter() { + public boolean testAttribute(Object target, String name, String value) { + return "label".equals(name) // //$NON-NLS-1$ + && value.equals(getStub(adaptable, LabelProviderStub.class).getLabel()); + } + + private <T> T getStub(final Object o, Class<T> type) { + // can't use IWorkbenchAdapter here because it can cause recursion + return (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {type}, // + new InvocationHandler() { + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + try { + Method method = o.getClass().getDeclaredMethod(m.getName(), m.getParameterTypes()); + return method.invoke(o, args); + } catch(RuntimeException ex) { + return null; + } catch(Exception ex) { + return null; + } + } + }); + } + }; + } + + /** + * A stub interface to access org.eclipse.jdt.internal.ui.packageview.ClassPathContainer#getLabel() + */ + public interface LabelProviderStub { + public String getLabel(); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsoleFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsoleFactory.java new file mode 100644 index 00000000..29b62508 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsoleFactory.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal; + +import org.eclipse.ui.console.IConsoleFactory; + +import org.eclipse.m2e.core.MavenPlugin; + +/** + * Maven Console factory is used to show the console from the "Open Console" + * drop-down action in Console view. + * + * @see org.eclipse.ui.console.consoleFactory extension point. + * + * @author Eugene Kuleshov + */ +public class MavenConsoleFactory implements IConsoleFactory { + + public void openConsole() { + MavenPlugin.getDefault().getConsole().showConsole(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsolePageParticipant.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsolePageParticipant.java new file mode 100644 index 00000000..a8154d4b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenConsolePageParticipant.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleConstants; +import org.eclipse.ui.console.IConsolePageParticipant; +import org.eclipse.ui.part.IPageBookViewPage; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.actions.MavenConsoleRemoveAction; +import org.eclipse.m2e.core.actions.MavenDebugOutputAction; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + + +public class MavenConsolePageParticipant implements IConsolePageParticipant { + + private IAction consoleRemoveAction; + private IAction debugAction; + private IAction showOnErrorAction; + private IAction showOnOutputAction; + + private static final String SHOW_ON_OUTPUT_LBL = Messages.MavenConsolePageParticipant_any; + private static final String SHOW_ON_ERR_LBL = Messages.MavenConsolePageParticipant_error; + + public void init(IPageBookViewPage page, IConsole console) { + this.consoleRemoveAction = new MavenConsoleRemoveAction(); + this.debugAction = new MavenDebugOutputAction(); + + + showOnOutputAction = new ShowOnOutputAction(console, SHOW_ON_OUTPUT_LBL); + showOnErrorAction = new ShowOnErrorAction(console, SHOW_ON_ERR_LBL); + + IActionBars actionBars = page.getSite().getActionBars(); + configureToolBar(actionBars.getToolBarManager()); + } + + private void configureToolBar(IToolBarManager mgr){ + mgr.appendToGroup(IConsoleConstants.LAUNCH_GROUP, consoleRemoveAction); + mgr.prependToGroup(IConsoleConstants.OUTPUT_GROUP, debugAction); + mgr.appendToGroup(IConsoleConstants.OUTPUT_GROUP, showOnOutputAction); + mgr.appendToGroup(IConsoleConstants.OUTPUT_GROUP, showOnErrorAction); + } + public void dispose() { + this.consoleRemoveAction = null; + this.debugAction = null; + } + + public void activated() { + } + + public void deactivated() { + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + return null; + } + + + + class ShowOnErrorAction extends MavenShowConsoleAction{ + public ShowOnErrorAction(IConsole console, String name){ + super(name); + setImageDescriptor(MavenImages.SHOW_CONSOLE_ERR); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.MavenShowConsoleAction#getKey() + */ + protected String getKey() { + return MavenPreferenceConstants.P_SHOW_CONSOLE_ON_ERR; + } + } + + class ShowOnOutputAction extends MavenShowConsoleAction{ + + /** + * @param console + */ + public ShowOnOutputAction(IConsole console, String name) { + super(name); + setImageDescriptor(MavenImages.SHOW_CONSOLE_OUT); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.MavenShowConsoleAction#getKey() + */ + protected String getKey() { + return MavenPreferenceConstants.P_SHOW_CONSOLE_ON_OUTPUT; + } + + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenShowConsoleAction.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenShowConsoleAction.java new file mode 100644 index 00000000..42275036 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenShowConsoleAction.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +import org.eclipse.m2e.core.MavenPlugin; + +/** + * MavenShowConsoleAction + * + * @author dyocum + */ +public abstract class MavenShowConsoleAction extends Action implements IPropertyChangeListener{ + + public MavenShowConsoleAction(String name){ + super(name, IAction.AS_CHECK_BOX); + setToolTipText(name); + getPreferenceStore().addPropertyChangeListener(this); + update(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + String property = event.getProperty(); + if (property.equals(getKey())) { + update(); + } + } + + protected abstract String getKey(); + + private void update() { + IPreferenceStore store = getPreferenceStore(); + if (store.getBoolean(getKey())) { + // on + setChecked(true); + } else { + // off + setChecked(false); + } + } + + /** + * @return + */ + private IPreferenceStore getPreferenceStore() { + return MavenPlugin.getDefault().getPreferenceStore(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.action.Action#run() + */ + public void run() { + IPreferenceStore store = getPreferenceStore(); + boolean show = isChecked(); + store.removePropertyChangeListener(this); + store.setValue(getKey(), show); + store.addPropertyChangeListener(this); + } + + /** + * Must be called to dispose this action. + */ + public void dispose() { + getPreferenceStore().removePropertyChangeListener(this); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenVersionDecorator.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenVersionDecorator.java new file mode 100644 index 00000000..c70fa53e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/MavenVersionDecorator.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.viewers.ILabelDecorator; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; +import org.eclipse.m2e.core.project.MavenProjectManager; + +/** + * @author Eugene Kuleshov + */ +public class MavenVersionDecorator implements ILabelDecorator { + + private Map<ILabelProviderListener, IMavenProjectChangedListener> listeners = new HashMap<ILabelProviderListener, IMavenProjectChangedListener>(); + + public Image decorateImage(Image image, Object element) { + return null; + } + + public String decorateText(String text, Object element) { + if(element instanceof IResource) { + IResource resource = (IResource) element; + IProject project = resource.getProject(); + if(project!=null) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + IMavenProjectFacade facade = projectManager.create(project, new NullProgressMonitor()); + if(facade!=null) { + ArtifactKey mavenProject = facade.getArtifactKey(); + if(mavenProject!=null) { + String name = resource.getName(); + int start = text.indexOf(name); + if(start>-1) { + int n = text.indexOf(' ', start + name.length()); + if(n>-1) { + return text.substring(0, n) + " " + mavenProject.getVersion() + text.substring(n); //$NON-NLS-1$ + } + } + return text + " " + mavenProject.getVersion(); //$NON-NLS-1$ + } + } + } + } + return null; + } + + public boolean isLabelProperty(Object element, String property) { + return false; + } + + public void addListener(final ILabelProviderListener listener) { + IMavenProjectChangedListener projectChangeListener = new IMavenProjectChangedListener() { + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + ArrayList<IResource> pomList = new ArrayList<IResource>(); + for(int i = 0; i < events.length; i++ ) { + // pomList.add(events[i].getSource()); + if(events[i]!=null && events[i].getMavenProject()!=null) { + IFile pom = events[i].getMavenProject().getPom(); + pomList.add(pom); + if(pom.getParent().getType()==IResource.PROJECT) { + pomList.add(pom.getParent()); + } + } + } + listener.labelProviderChanged(new LabelProviderChangedEvent(MavenVersionDecorator.this, pomList.toArray())); + } + }; + + listeners.put(listener, projectChangeListener); + + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + projectManager.addMavenProjectChangedListener(projectChangeListener); + } + + public void removeListener(ILabelProviderListener listener) { + IMavenProjectChangedListener projectChangeListener = listeners.get(listener); + if(projectChangeListener!=null) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + projectManager.removeMavenProjectChangedListener(projectChangeListener); + } + } + + public void dispose() { + // TODO remove all listeners + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/components/TextComboBoxCellEditor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/components/TextComboBoxCellEditor.java new file mode 100644 index 00000000..45c0e957 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/components/TextComboBoxCellEditor.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.components; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + + +/** + * A TextComboBoxCellEditor to overcome the limitation of the standard ComboBoxCellEditor, which does not allow to edit + * plain text values. + * + * @author Dmitry Platonoff + */ +public class TextComboBoxCellEditor extends CellEditor { + + protected String[] items; + + + protected CCombo combo; + + public TextComboBoxCellEditor(Composite parent, int style) { + super(parent, style); + } + + protected Control createControl(Composite parent) { + combo = new CCombo(parent, getStyle()); + combo.setFont(parent.getFont()); + + combo.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + keyReleaseOccured(e); + } + }); + combo.addTraverseListener(new TraverseListener() { + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_ESCAPE + || e.detail == SWT.TRAVERSE_RETURN) { + e.doit = false; + } + } + }); + + loadItems(); + + return combo; + } + + protected Object doGetValue() { + Assert.isNotNull(combo); + return combo.getText(); + } + + protected void doSetFocus() { + Assert.isNotNull(combo); + combo.setFocus(); + } + + protected void doSetValue(Object value) { + Assert.isNotNull(combo); + combo.setText(String.valueOf(value)); + } + + public String[] getItems() { + return items; + } + + public void setItems(String[] items) { + this.items = items; + loadItems(); + } + + protected void loadItems() { + if(combo != null && items != null) { + combo.setItems(items); + } + } + + protected void keyReleaseOccured(KeyEvent keyEvent) { + if(keyEvent.character == SWT.ESC) { + fireCancelEditor(); + } else if(keyEvent.character == SWT.TAB || keyEvent.character == SWT.CR) { + focusLost(); + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractLifecyclePropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractLifecyclePropertyPage.java new file mode 100644 index 00000000..3ce330f1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractLifecyclePropertyPage.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import org.eclipse.core.resources.IProject; +import org.eclipse.swt.widgets.Shell; + +/** + * AbstractLifecyclePropertyPage + * Holds the pieces used in the common lifecycle mapping properties pages. + * + * @author dyocum + */ +public abstract class AbstractLifecyclePropertyPage extends AbstractPropertyPageExtensionPoint implements ILifecyclePropertyPage{ + private IProject project; + private Shell shell; + + public AbstractLifecyclePropertyPage(){ + } + + public void setupPage(IProject project, Shell shell){ + this.project = project; + this.setShell(shell); + } + + public void setProject(IProject project){ + this.project = project; + } + + public IProject getProject(){ + return project; + } + + /** + * @param shell The shell to set. + */ + public void setShell(Shell shell) { + this.shell = shell; + } + + /** + * @return Returns the shell. + */ + public Shell getShell() { + return shell; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractPropertyPageExtensionPoint.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractPropertyPageExtensionPoint.java new file mode 100644 index 00000000..ace3c250 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/AbstractPropertyPageExtensionPoint.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +/** + * AbstractPropertyPageExtensionPoint + * + * @author dyocum + */ +public class AbstractPropertyPageExtensionPoint implements ILifecyclePropertyPageExtensionPoint { + + private String name; + private String id; + private String lifecycleMappingId; + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#getLifecycleMappingId() + */ + public String getLifecycleMappingId() { + return lifecycleMappingId; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#getName() + */ + public String getName() { + return name; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#getPageId() + */ + public String getPageId() { + return id; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#setLifecycleMappingId(java.lang.String) + */ + public void setLifecycleMappingId(String lifecycleMappingId) { + this.lifecycleMappingId = lifecycleMappingId; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#setName() + */ + public void setName(String name) { + this.name = name; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.ILifecyclePropertyPageExtensionPoint#setPageId() + */ + public void setPageId(String id) { + this.id = id; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPage.java new file mode 100644 index 00000000..bbc88239 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPage.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import org.eclipse.core.resources.IProject; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +/** + * ILifecyclePropertyPage + * + * @author dyocum + */ +public interface ILifecyclePropertyPage extends ILifecyclePropertyPageExtensionPoint{ + /** + * Create and return the composite which will be shown in the parent properties page. + * @param parent + * @return + */ + public Control createContents(Composite parent); + + /** + * Called when the 'Restore Defaults' button is pressed in the properties page. + */ + public void performDefaults(); + + /** + * Called when the 'OK' or 'Apply' buttons are pressed in the properties dialog. + * @return + */ + public boolean performOk(); + + /** + * The project that these lifecycle mapping properties apply to + * @param project + */ + public void setProject(IProject project); + + public IProject getProject(); + + /** + * The parent shell used for showing error messages. + * @param shell + */ + public void setShell(Shell shell); + + public Shell getShell(); + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPageExtensionPoint.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPageExtensionPoint.java new file mode 100644 index 00000000..a81284da --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ILifecyclePropertyPageExtensionPoint.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +/** + * ILifecyclePropertyPageExtensionPoint + * + * @author dyocum + */ +public interface ILifecyclePropertyPageExtensionPoint { + /** + * The name of the page. This will be displayed in the title of the properties page. + * @return + */ + public String getName(); + + /** + * Name of the page, called when the extension point is read in + */ + public void setName(String name); + + /** + * Get the id of the property page as defined in the extension point + * @return + */ + public String getPageId(); + + /** + * Set the id of the property page, called when extension point is read + */ + public void setPageId(String id); + + /** + * Sets the id of the lifecycle mapping strategy that this property page is + * associated with + * @param lifecycleMappingId + */ + public void setLifecycleMappingId(String lifecycleMappingId); + + /** + * Gets the id of the lifecycle mapping strategy + * @return + */ + public String getLifecycleMappingId(); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/LifecycleMappingPropertyPageFactory.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/LifecycleMappingPropertyPageFactory.java new file mode 100644 index 00000000..2be8b931 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/LifecycleMappingPropertyPageFactory.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.ResolverConfiguration; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + + +/** + * LifecycleMappingPropertyPageFactory + * + * @author dyocum + */ +public class LifecycleMappingPropertyPageFactory { + + public static final String EXTENSION_LIFECYCLE_MAPPING_PROPERTY_PAGE = IMavenConstants.PLUGIN_ID + ".lifecycleMappingPropertyPage"; //$NON-NLS-1$ + + private static final String ATTR_LIFECYCLE_MAPPING_ID = "lifecycleMappingId"; //$NON-NLS-1$ + + private static final String ATTR_LIFECYCLE_PROP_NAME = "name"; //$NON-NLS-1$ + + private static final String ATTR_LIFECYCLE_PROP_ID = "id"; //$NON-NLS-1$ + + private static final String ELEMENT_LIFECYCLE_MAPPING_PROPERTY_PAGE = "lifecycleMappingPropertyPage"; //$NON-NLS-1$ + + private static LifecycleMappingPropertyPageFactory factory; + + private Map<String, ILifecyclePropertyPage> pageMap; + + public static LifecycleMappingPropertyPageFactory getFactory() { + if(factory == null) { + factory = new LifecycleMappingPropertyPageFactory(); + factory.buildFactory(); + } + return factory; + } + + /** + * Get a particular lifecycle property page, set in the project to use for the lifecycle mapping, set the Shell for + * displaying dialogs. + * + * @param id + * @param project + * @param shell + * @return + */ + public ILifecyclePropertyPage getPageForId(String id, IProject project, Shell shell) { + if(id == null){ + //for the no-op (empty) lifecycle mapping, use that page + id = "NULL"; //$NON-NLS-1$ + } + ILifecyclePropertyPage page = getFactory().pageMap.get(id); + if(page == null){ + return null; + } + page.setProject(project); + page.setShell(shell); + return page; + } + + public ILifecyclePropertyPage getPage(String id){ + return getFactory().pageMap.get(id); + } + + public void buildFactory() { + pageMap = new HashMap<String, ILifecyclePropertyPage>(); + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint configuratorsExtensionPoint = registry.getExtensionPoint(EXTENSION_LIFECYCLE_MAPPING_PROPERTY_PAGE); + if(configuratorsExtensionPoint != null) { + IExtension[] configuratorExtensions = configuratorsExtensionPoint.getExtensions(); + for(IExtension extension : configuratorExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals(ELEMENT_LIFECYCLE_MAPPING_PROPERTY_PAGE)) { + try { + Object o = element.createExecutableExtension("class"); //$NON-NLS-1$ + ILifecyclePropertyPage propPage = (ILifecyclePropertyPage) o; + String id = element.getAttribute(ATTR_LIFECYCLE_MAPPING_ID); + + propPage.setLifecycleMappingId(id); + String name = element.getAttribute(ATTR_LIFECYCLE_PROP_NAME); + propPage.setName(name); + + String pageId = element.getAttribute(ATTR_LIFECYCLE_PROP_ID); + if(pageId != null) { + propPage.setPageId(pageId); + } + pageMap.put(id, propPage); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + } + } + } + + public static IMavenProjectFacade getProjectFacade(IProject project) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + return projectManager.create(project, new NullProgressMonitor()); + } + + public static ResolverConfiguration getResolverConfiguration(IProject project) { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + return projectManager.getResolverConfiguration(project); + } + + public static ILifecycleMapping getLifecycleMapping(IProject project) throws CoreException { + IMavenProjectFacade facade = getProjectFacade(project); + ILifecycleMapping lifecycleMapping = null; + IProjectConfigurationManager configurationManager = MavenPlugin.getDefault().getProjectConfigurationManager(); + lifecycleMapping = configurationManager.getLifecycleMapping(facade, new NullProgressMonitor()); + return lifecycleMapping; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTable.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTable.java new file mode 100644 index 00000000..806aeebb --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTable.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; + +/** + * ProjectConfiguratorsTable + * Composite that holds a read only table of project configurators for a given lifecycle mapping strategy. + * + * @author dyocum + */ +public class ProjectConfiguratorsTable { + + private TableViewer configuratorsTable; + private ProjectConfiguratorsTableContentProvider configuratorsContentProvider; + private ProjectConfiguratorsTableLabelProvider configuratorsLabelProvider; + public static final String[] CONFIG_TABLE_COLUMN_PROPERTIES = new String[]{ "name", "id"}; //$NON-NLS-1$ //$NON-NLS-2$ + public static final String[] CONFIG_TABLE_COLUMN_NAMES = new String[]{ Messages.ProjectConfiguratorsTable_column_name, Messages.ProjectConfiguratorsTable_column_id}; + private static final int TABLE_WIDTH = 500; + + public ProjectConfiguratorsTable(Composite parent, IProject project){ + createTable(parent); + updateTable(project); + } + + protected void updateTable(IProject project){ + if(project != null){ + try{ + ILifecycleMapping mapping = LifecycleMappingPropertyPageFactory.getLifecycleMapping(project); + List<AbstractProjectConfigurator> projectConfigurators = mapping.getProjectConfigurators(LifecycleMappingPropertyPageFactory.getProjectFacade(project), new NullProgressMonitor()); + setProjectConfigurators(projectConfigurators.toArray(new AbstractProjectConfigurator[]{})); + } catch(CoreException e){ + setProjectConfigurators(new AbstractProjectConfigurator[]{}); + } + } + } + private void createTable(Composite parent){ + configuratorsTable = new TableViewer(parent, SWT.BORDER|SWT.H_SCROLL|SWT.V_SCROLL); + TableViewerColumn nameColumn = new TableViewerColumn(configuratorsTable, SWT.LEFT); + nameColumn.getColumn().setText(CONFIG_TABLE_COLUMN_NAMES[0]); + nameColumn.getColumn().setWidth((int)(TABLE_WIDTH*.50)); + + TableViewerColumn idColumn = new TableViewerColumn(configuratorsTable, SWT.LEFT); + idColumn.getColumn().setText(CONFIG_TABLE_COLUMN_NAMES[1]); + idColumn.getColumn().setWidth((int)(TABLE_WIDTH*.50)); + + configuratorsTable.getTable().setHeaderVisible(true); + configuratorsTable.getTable().setLinesVisible(true); + configuratorsContentProvider = new ProjectConfiguratorsTableContentProvider(); + configuratorsLabelProvider = new ProjectConfiguratorsTableLabelProvider(); + configuratorsTable.setContentProvider(configuratorsContentProvider); + configuratorsTable.setLabelProvider(configuratorsLabelProvider); + configuratorsTable.setColumnProperties(CONFIG_TABLE_COLUMN_PROPERTIES); + configuratorsTable.getTable().setData("name", "projectConfiguratorsTable"); //$NON-NLS-1$ //$NON-NLS-2$ + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1); + gd.horizontalIndent=6; + gd.grabExcessHorizontalSpace = true; + gd.grabExcessVerticalSpace = true; + configuratorsTable.getControl().setLayoutData(gd); + + final TableColumn nCol = nameColumn.getColumn(); + final TableColumn iCol = idColumn.getColumn(); + final Table tab = configuratorsTable.getTable(); + configuratorsTable.getTable().addControlListener(new ControlAdapter() { + public void controlResized(ControlEvent e) { + nCol.setWidth((int)(tab.getClientArea().width*0.50)); + iCol.setWidth((int)(tab.getClientArea().width*0.50)); + } + }); + } + + public TableViewer getTableViewer(){ + return configuratorsTable; + } + + public void setProjectConfigurators(AbstractProjectConfigurator[] configurators){ + configuratorsTable.setInput(configurators); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableContentProvider.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableContentProvider.java new file mode 100644 index 00000000..72fb20ac --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableContentProvider.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; + +/** + * ConfiguratorsTableContentProvider + * + * @author dyocum + */ +public class ProjectConfiguratorsTableContentProvider implements IStructuredContentProvider { + + public ProjectConfiguratorsTableContentProvider(){ + } + + protected String[] getNoConfigMsg(){ + return new String[]{Messages.ProjectConfiguratorsTableContentProvider_no_configs}; + } + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + + if(inputElement == null || !(inputElement instanceof AbstractProjectConfigurator[]) || ((AbstractProjectConfigurator[])inputElement).length == 0){ + return getNoConfigMsg(); + } + return (AbstractProjectConfigurator[])inputElement; + + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + // TODO Auto-generated method dispose + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // TODO Auto-generated method inputChanged + + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableLabelProvider.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableLabelProvider.java new file mode 100644 index 00000000..92fee588 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/lifecycle/ProjectConfiguratorsTableLabelProvider.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.lifecycle; + +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator; + +/** + * ConfiguratorsTableLabelProvider + * + * @author dyocum + */ +public class ProjectConfiguratorsTableLabelProvider implements ITableLabelProvider, IColorProvider{ + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) + */ + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) + */ + public String getColumnText(Object element, int columnIndex) { + if(element == null){ + return ""; //$NON-NLS-1$ + } else if(element instanceof AbstractProjectConfigurator){ + return columnIndex == 0 ? ((AbstractProjectConfigurator)element).getName() : ((AbstractProjectConfigurator)element).getId(); + } + return columnIndex == 0 ? element.toString() : ""; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void addListener(ILabelProviderListener listener) { + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() + */ + public void dispose() { + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) + */ + public boolean isLabelProperty(Object element, String property) { + // TODO Auto-generated method isLabelProperty + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void removeListener(ILabelProviderListener listener) { + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object) + */ + public Color getBackground(Object element) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object) + */ + public Color getForeground(Object element) { + if(element instanceof AbstractProjectConfigurator){ + return Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + } + return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/CustomizableLifecycleMappingPropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/CustomizableLifecycleMappingPropertyPage.java new file mode 100644 index 00000000..7f714460 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/CustomizableLifecycleMappingPropertyPage.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.ui.internal.lifecycle.AbstractLifecyclePropertyPage; +import org.eclipse.m2e.core.ui.internal.lifecycle.ProjectConfiguratorsTable; + +/** + * CustomizableLifecycleMappingPropertyPage + * + * @author dyocum + */ +public class CustomizableLifecycleMappingPropertyPage extends AbstractLifecyclePropertyPage{ + + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite) + */ + public Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(GridData.FILL)); + new ProjectConfiguratorsTable(composite, getProject()); + return composite; + } + + public void performDefaults(){ + //do nothing + } + + public boolean performOk() { + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.lifecycle.AbstractLifecyclePropertyPage#getMessage() + */ + public String getMessage() { + // TODO Auto-generated method getMessage + return Messages.CustomizableLifecycleMappingPropertyPage_message; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/EmptyLifecycleMappingPropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/EmptyLifecycleMappingPropertyPage.java new file mode 100644 index 00000000..92c98101 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/EmptyLifecycleMappingPropertyPage.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.m2e.core.internal.Messages; + +/** + * EmptyLifecycleMappingPropertyPage + * + * @author dyocum + */ +public class EmptyLifecycleMappingPropertyPage extends SimpleLifecycleMappingPropertyPage { + + public EmptyLifecycleMappingPropertyPage() { + super(Messages.EmptyLifecycleMappingPropertyPage_title); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/GoalsFieldEditor.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/GoalsFieldEditor.java new file mode 100644 index 00000000..480f0dad --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/GoalsFieldEditor.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; + + +/** + * A field editor for a combo box that allows the drop-down selection of one of + * a list of items. + * + * Adapted from org.eclipse.jface.preference.ComboFieldEditor + */ +public class GoalsFieldEditor extends FieldEditor { + + /** + * The <code>Combo</code> widget. + */ + Text goalsText; + + /** + * The value (not the name) of the currently selected item in the Combo widget. + */ + String value; + + private Button goialsSelectButton; + + private final String buttonText; + + /** + * Create the combo box field editor. + * + * @param name the name of the preference this field editor works on + * @param labelText the label text of the field editor + * @param buttonText + * @param entryValues the entry values + * @param parent the parent composite + */ + public GoalsFieldEditor(String name, String labelText, String buttonText, Composite parent) { + init(name, labelText); + this.buttonText = buttonText; + createControl(parent); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int) + */ + protected void adjustForNumColumns(int numColumns) { + if(numColumns > 1) { + Control control = getLabelControl(); + ((GridData) control.getLayoutData()).horizontalSpan = numColumns; + ((GridData) goalsText.getLayoutData()).horizontalSpan = numColumns - 1; + } else { + Control control = getLabelControl(); + ((GridData) control.getLayoutData()).horizontalSpan = 2; + ((GridData) goalsText.getLayoutData()).horizontalSpan = 1; + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite, int) + */ + protected void doFillIntoGrid(Composite parent, int numColumns) { + Control labelControl = getLabelControl(parent); + GridData gd = new GridData(); + gd.horizontalSpan = numColumns; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + labelControl.setLayoutData(gd); + + Text goalsText = getTextControl(parent); + gd = new GridData(); + gd.horizontalSpan = numColumns - 1; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + goalsText.setLayoutData(gd); + goalsText.setFont(parent.getFont()); + + goialsSelectButton = new Button(parent, SWT.NONE); + goialsSelectButton.setText(buttonText); + goialsSelectButton.addSelectionListener(new MavenGoalSelectionAdapter(goalsText, parent.getShell())); + gd = new GridData(); + gd.horizontalSpan = 1; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + goalsText.setLayoutData(gd); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#doLoad() + */ + protected void doLoad() { + updateComboForValue(getPreferenceStore().getString(getPreferenceName())); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault() + */ + protected void doLoadDefault() { + updateComboForValue(getPreferenceStore().getDefaultString(getPreferenceName())); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#doStore() + */ + protected void doStore() { + if (value == null) { + getPreferenceStore().setToDefault(getPreferenceName()); + } else { + getPreferenceStore().setValue(getPreferenceName(), value); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls() + */ + public int getNumberOfControls() { + return 2; + } + + /* + * Lazily create and return the Combo control. + */ + private Text getTextControl(Composite parent) { + if (goalsText == null) { + goalsText = new Text(parent, SWT.BORDER); + goalsText.setFont(parent.getFont()); +// for (int i = 0; i < entryValues.length; i++) { +// goalsCombo.add(entryValues[i], i); +// } +// goalsCombo.addSelectionListener(new SelectionAdapter() { +// public void widgetSelected(SelectionEvent evt) { +// String oldValue = value; +// value = goalsCombo.getText(); +// setPresentsDefaultValue(false); +// fireValueChanged(VALUE, oldValue, value); +// } +// }); + goalsText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent modifyevent) { + String oldValue = value; + value = goalsText.getText(); + setPresentsDefaultValue(false); + fireValueChanged(VALUE, oldValue, value); + } + }); + } + return goalsText; + } + + protected void setPresentsDefaultValue(boolean booleanValue) { + super.setPresentsDefaultValue(booleanValue); + } + + protected void fireValueChanged(String property, Object oldValue, Object newValue) { + super.fireValueChanged(property, oldValue, newValue); + } + +// /* +// * Given the name (label) of an entry, return the corresponding value. +// */ +// String getValueForName(String name) { +// for (int i = 0; i < fEntryValues.length; i++) { +// String[] entry = fEntryValues[i]; +// if (name.equals(entry[0])) { +// return entry[1]; +// } +// } +// return fEntryValues[0][0]; +// } + + /* + * Set the name in the combo widget to match the specified value. + */ + private void updateComboForValue(String value) { + this.value = value; + goalsText.setText(value); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/LocalArchetypeCatalogDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/LocalArchetypeCatalogDialog.java new file mode 100644 index 00000000..52c49d57 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/LocalArchetypeCatalogDialog.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.archetype.catalog.ArchetypeCatalog; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.LocalCatalogFactory; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Local Archetype catalog dialog + * + * @author Eugene Kuleshov + */ +public class LocalArchetypeCatalogDialog extends TitleAreaDialog { + + private static final String DIALOG_SETTINGS = LocalArchetypeCatalogDialog.class.getName(); + + private static final String KEY_LOCATIONS = "catalogLocation"; //$NON-NLS-1$ + + private static final int MAX_HISTORY = 15; + + private String title; + + private String message; + + Combo catalogLocationCombo; + + private Text catalogDescriptionText; + + private IDialogSettings dialogSettings; + + private ArchetypeCatalogFactory archetypeCatalogFactory; + + + protected LocalArchetypeCatalogDialog(Shell shell, ArchetypeCatalogFactory factory) { + super(shell); + this.archetypeCatalogFactory = factory; + this.title = Messages.LocalArchetypeCatalogDialog_title; + this.message = Messages.LocalArchetypeCatalogDialog_message; + setShellStyle(SWT.DIALOG_TRIM); + + IDialogSettings pluginSettings = MavenPlugin.getDefault().getDialogSettings(); + dialogSettings = pluginSettings.getSection(DIALOG_SETTINGS); + if(dialogSettings == null) { + dialogSettings = new DialogSettings(DIALOG_SETTINGS); + pluginSettings.addSection(dialogSettings); + } + } + + protected Control createContents(Composite parent) { + Control control = super.createContents(parent); + setTitle(title); + setMessage(message); + return control; + } + + protected Control createDialogArea(Composite parent) { + Composite composite1 = (Composite) super.createDialogArea(parent); + + Composite composite = new Composite(composite1, SWT.NONE); + composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginTop = 7; + gridLayout.marginWidth = 12; + gridLayout.numColumns = 3; + composite.setLayout(gridLayout); + + Label catalogLocationLabel = new Label(composite, SWT.NONE); + catalogLocationLabel.setText(Messages.LocalArchetypeCatalogDialog_lblCatalog); + + catalogLocationCombo = new Combo(composite, SWT.NONE); + GridData gd_catalogLocationCombo = new GridData(SWT.FILL, SWT.CENTER, true, false); + gd_catalogLocationCombo.widthHint = 250; + catalogLocationCombo.setLayoutData(gd_catalogLocationCombo); + catalogLocationCombo.setItems(getSavedValues(KEY_LOCATIONS)); + + Button browseButton = new Button(composite, SWT.NONE); + browseButton.setText(Messages.LocalArchetypeCatalogDialog_btnBrowse); + browseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + FileDialog dialog = new FileDialog(getShell()); + dialog.setText(Messages.LocalArchetypeCatalogDialog_dialog_title); + String location = dialog.open(); + if(location!=null) { + catalogLocationCombo.setText(location); + update(); + } + } + }); + setButtonLayoutData(browseButton); + + Label catalogDescriptionLabel = new Label(composite, SWT.NONE); + catalogDescriptionLabel.setText(Messages.LocalArchetypeCatalogDialog_lblDesc); + + catalogDescriptionText = new Text(composite, SWT.BORDER); + catalogDescriptionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + + if(archetypeCatalogFactory!=null) { + catalogLocationCombo.setText(archetypeCatalogFactory.getId()); + catalogDescriptionText.setText(archetypeCatalogFactory.getDescription()); + } + + ModifyListener modifyListener = new ModifyListener() { + public void modifyText(final ModifyEvent e) { + update(); + } + }; + catalogLocationCombo.addModifyListener(modifyListener); + catalogDescriptionText.addModifyListener(modifyListener); + + return composite; + } + + private String[] getSavedValues(String key) { + String[] array = dialogSettings.getArray(key); + return array == null ? new String[0] : array; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + public void create() { + super.create(); + getButton(IDialogConstants.OK_ID).setEnabled(false); + } + + protected void okPressed() { + String description = catalogDescriptionText.getText().trim(); + String location = catalogLocationCombo.getText().trim(); + + archetypeCatalogFactory = new LocalCatalogFactory(location, description, true); + + saveValue(KEY_LOCATIONS, location); + + super.okPressed(); + } + + public ArchetypeCatalogFactory getArchetypeCatalogFactory() { + return archetypeCatalogFactory; + } + + private void saveValue(String key, String value) { + List<String> dirs = new ArrayList<String>(); + dirs.addAll(Arrays.asList(getSavedValues(key))); + + dirs.remove(value); + dirs.add(0, value); + + if(dirs.size() > MAX_HISTORY) { + dirs = dirs.subList(0, MAX_HISTORY); + } + + dialogSettings.put(key, dirs.toArray(new String[dirs.size()])); + } + + void update() { + boolean isValid = isValid(); + // verifyButton.setEnabled(isValid); + getButton(IDialogConstants.OK_ID).setEnabled(isValid); + } + + private boolean isValid() { + setErrorMessage(null); + setMessage(null, IStatus.WARNING); + + String location = catalogLocationCombo.getText().trim(); + if(location.length()==0) { + setErrorMessage(Messages.LocalArchetypeCatalogDialog_error_no_location); + return false; + } + + if(!new File(location).exists()) { + setErrorMessage(Messages.LocalArchetypeCatalogDialog_error_exist); + return false; + } + + LocalCatalogFactory factory = new LocalCatalogFactory(location, null, true); + ArchetypeCatalog archetypeCatalog = factory.getArchetypeCatalog(); + @SuppressWarnings("unchecked") + List<Archetype> archetypes = archetypeCatalog.getArchetypes(); + if(archetypes==null || archetypes.size()==0) { + setMessage(Messages.LocalArchetypeCatalogDialog_error_empty, IStatus.WARNING); + } + + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenArchetypesPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenArchetypesPreferencePage.java new file mode 100644 index 00000000..18b4f125 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenArchetypesPreferencePage.java @@ -0,0 +1,320 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWebBrowser; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.LocalCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.RemoteCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeManager; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Maven Archetype catalogs preference page + * + * @author Eugene Kuleshov + */ +public class MavenArchetypesPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + ArchetypeManager archetypeManager; + TableViewer archetypesViewer; + + List<ArchetypeCatalogFactory> archetypeCatalogs; + + public MavenArchetypesPreferencePage() { + setTitle(Messages.MavenArchetypesPreferencePage_title); + + this.archetypeManager = MavenPlugin.getDefault().getArchetypeManager(); + } + + protected void performDefaults() { + for(Iterator<ArchetypeCatalogFactory> it = archetypeCatalogs.iterator(); it.hasNext();) { + ArchetypeCatalogFactory factory = it.next(); + if(factory.isEditable()) { + it.remove(); + } + } + + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.setSelection(null, true); + + super.performDefaults(); + } + + public boolean performOk() { + Collection<ArchetypeCatalogFactory> catalogs = archetypeManager.getArchetypeCatalogs(); + for(ArchetypeCatalogFactory factory : catalogs) { + if(factory.isEditable()) { + archetypeManager.removeArchetypeCatalogFactory(factory.getId()); + } + } + for(ArchetypeCatalogFactory factory : archetypeCatalogs) { + if(factory.isEditable()) { + archetypeManager.addArchetypeCatalogFactory(factory); + } + } + + try { + archetypeManager.saveCatalogs(); + } catch(IOException ex) { + setErrorMessage(NLS.bind(Messages.MavenArchetypesPreferencePage_error, ex.getMessage())); + return false; + } + + return super.performOk(); + } + + public void init(IWorkbench workbench) { + } + + protected Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(2, false); + gridLayout.marginWidth = 0; + gridLayout.marginHeight = 0; + composite.setLayout(gridLayout); + + Link addRemoveOrLink = new Link(composite, SWT.NONE); + GridData gd_addRemoveOrLink = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1); + addRemoveOrLink.setLayoutData(gd_addRemoveOrLink); + addRemoveOrLink.setText(Messages.MavenArchetypesPreferencePage_link); + addRemoveOrLink.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + try { + URL url = new URL("http://maven.apache.org/plugins/maven-archetype-plugin/specification/archetype-catalog.html"); //$NON-NLS-1$ + IWebBrowser browser = PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser(); + browser.openURL(url); + } catch(MalformedURLException ex) { + MavenLogger.log("Malformed URL", ex); + } catch(PartInitException ex) { + MavenLogger.log(ex); + } + } + }); + + // archetypesViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER | SWT.FULL_SELECTION); + archetypesViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION); + + archetypesViewer.setLabelProvider(new CatalogsLabelProvider()); + + archetypesViewer.setContentProvider(new IStructuredContentProvider() { + + public Object[] getElements(Object input) { + if(input instanceof Collection) { + return ((Collection<?>) input).toArray(); + } + return new Object[0]; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + + }); + + Table table = archetypesViewer.getTable(); + table.setLinesVisible(false); + table.setHeaderVisible(false); + table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 4)); + + TableColumn typeColumn = new TableColumn(table, SWT.NONE); + typeColumn.setWidth(250); + typeColumn.setText(""); //$NON-NLS-1$ + + Button addLocalButton = new Button(composite, SWT.NONE); + addLocalButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + addLocalButton.setText(Messages.MavenArchetypesPreferencePage_btnAddLocal); + addLocalButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + LocalArchetypeCatalogDialog dialog = new LocalArchetypeCatalogDialog(getShell(), null); + if (dialog.open()==Window.OK) { + ArchetypeCatalogFactory factory = dialog.getArchetypeCatalogFactory(); + archetypeCatalogs.add(factory); + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.setSelection(new StructuredSelection(factory), true); + } + } + }); + + Button addRemoteButton = new Button(composite, SWT.NONE); + addRemoteButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + addRemoteButton.setText(Messages.MavenArchetypesPreferencePage_btnAddRemote); + addRemoteButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + RemoteArchetypeCatalogDialog dialog = new RemoteArchetypeCatalogDialog(getShell(), null); + if (dialog.open()==Window.OK) { + ArchetypeCatalogFactory factory = dialog.getArchetypeCatalogFactory(); + archetypeCatalogs.add(factory); + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.setSelection(new StructuredSelection(factory), true); + } + } + }); + + final Button editButton = new Button(composite, SWT.NONE); + editButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + editButton.setEnabled(false); + editButton.setText(Messages.MavenArchetypesPreferencePage_btnEdit); + editButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + ArchetypeCatalogFactory factory = getSelectedArchetypeCatalogFactory(); + ArchetypeCatalogFactory newFactory = null; + if(factory instanceof LocalCatalogFactory) { + LocalArchetypeCatalogDialog dialog = new LocalArchetypeCatalogDialog(getShell(), factory); + if (dialog.open()==Window.OK) { + newFactory = dialog.getArchetypeCatalogFactory(); + } + } else if(factory instanceof RemoteCatalogFactory) { + RemoteArchetypeCatalogDialog dialog = new RemoteArchetypeCatalogDialog(getShell(), factory); + if (dialog.open()==Window.OK) { + newFactory = dialog.getArchetypeCatalogFactory(); + } + } + if(newFactory!=null) { + int n = archetypeCatalogs.indexOf(factory); + if(n>-1) { + archetypeCatalogs.set(n, newFactory); + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.setSelection(new StructuredSelection(newFactory), true); + } + } + } + }); + + final Button removeButton = new Button(composite, SWT.NONE); + removeButton.setEnabled(false); + removeButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true)); + removeButton.setText(Messages.MavenArchetypesPreferencePage_btnRemove); + removeButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + ArchetypeCatalogFactory factory = getSelectedArchetypeCatalogFactory(); + archetypeCatalogs.remove(factory); + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.setSelection(null, true); + } + }); + + archetypesViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if(archetypesViewer.getSelection() instanceof IStructuredSelection) { + ArchetypeCatalogFactory factory = getSelectedArchetypeCatalogFactory(); + boolean isEnabled = factory != null && factory.isEditable(); + removeButton.setEnabled(isEnabled); + editButton.setEnabled(isEnabled); + } + } + }); + + archetypeCatalogs = new ArrayList<ArchetypeCatalogFactory>(archetypeManager.getArchetypeCatalogs()); + archetypesViewer.setInput(archetypeCatalogs); + archetypesViewer.refresh(); // should listen on property changes instead? + + return composite; + } + + protected ArchetypeCatalogFactory getSelectedArchetypeCatalogFactory() { + IStructuredSelection selection = (IStructuredSelection) archetypesViewer.getSelection(); + return (ArchetypeCatalogFactory) selection.getFirstElement(); + } + + + static class CatalogsLabelProvider implements ITableLabelProvider, IColorProvider { + + private Color disabledColor = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY); + + public String getColumnText(Object element, int columnIndex) { + ArchetypeCatalogFactory factory = (ArchetypeCatalogFactory) element; + if(factory instanceof LocalCatalogFactory) { + return NLS.bind(Messages.MavenArchetypesPreferencePage_local, factory.getDescription()); + } else if(factory instanceof RemoteCatalogFactory) { + if(factory.isEditable()) { + return NLS.bind(Messages.MavenArchetypesPreferencePage_remote,factory.getDescription()); + } + return NLS.bind(Messages.MavenArchetypesPreferencePage_packaged, factory.getDescription()); + } + return factory.getDescription(); + } + + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + public Color getBackground(Object element) { + return null; + } + + public Color getForeground(Object element) { + ArchetypeCatalogFactory factory = (ArchetypeCatalogFactory) element; + return !factory.isEditable() ? disabledColor : null; + } + + public void dispose() { + } + + public boolean isLabelProperty(Object element, String property) { + return false; + } + + public void addListener(ILabelProviderListener listener) { + } + + public void removeListener(ILabelProviderListener listener) { + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenGoalSelectionAdapter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenGoalSelectionAdapter.java new file mode 100644 index 00000000..25493b14 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenGoalSelectionAdapter.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.m2e.core.ui.dialogs.MavenGoalSelectionDialog; + +public class MavenGoalSelectionAdapter extends SelectionAdapter { + private Shell shell; + private Text text; + + public MavenGoalSelectionAdapter(Text text, Shell shell) { + this.text = text; + this.shell = shell; + } + + public void widgetSelected(SelectionEvent e) { +// String fileName = Util.substituteVar(fPomDirName.getText()); +// if(!isDirectoryExist(fileName)) { +// MessageDialog.openError(getShell(), Messages.getString("launch.errorPomMissing"), +// Messages.getString("launch.errorSelectPom")); //$NON-NLS-1$ //$NON-NLS-2$ +// return; +// } + MavenGoalSelectionDialog dialog = new MavenGoalSelectionDialog(shell); + int rc = dialog.open(); + if(rc == IDialogConstants.OK_ID) { + text.insert(""); // clear selected text //$NON-NLS-1$ + + String txt = text.getText(); + int len = txt.length(); + int pos = text.getCaretPosition(); + + StringBuffer sb = new StringBuffer(); + if((pos > 0 && txt.charAt(pos - 1) != ' ')) { + sb.append(' '); + } + + String sep = ""; //$NON-NLS-1$ + Object[] o = dialog.getResult(); + for(int i = 0; i < o.length; i++ ) { + if(o[i] instanceof MavenGoalSelectionDialog.Entry) { + if(dialog.isQualifiedName()) { + sb.append(sep).append(((MavenGoalSelectionDialog.Entry) o[i]).getQualifiedName()); + } else { + sb.append(sep).append(((MavenGoalSelectionDialog.Entry) o[i]).getName()); + } + } + sep = " "; //$NON-NLS-1$ + } + + if(pos < len && txt.charAt(pos) != ' ') { + sb.append(' '); + } + + text.insert(sb.toString()); + text.setFocus(); + } + } + }
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenInstallationsPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenInstallationsPreferencePage.java new file mode 100644 index 00000000..e17ffd28 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenInstallationsPreferencePage.java @@ -0,0 +1,677 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IPropertyListener; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWebBrowser; +import org.eclipse.ui.ide.IDE; + +import org.apache.maven.settings.building.SettingsProblem; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.MavenRuntime; +import org.eclipse.m2e.core.embedder.MavenRuntimeManager; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.embedder.MavenEmbeddedRuntime; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +/** + * Maven installations preference page + * + * @author Eugene Kuleshov + */ +public class MavenInstallationsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + final MavenPlugin mavenPlugin; + + final MavenRuntimeManager runtimeManager; + + final IMavenConfiguration mavenConfiguration; + + final IMaven maven; + + MavenRuntime defaultRuntime; + + List<MavenRuntime> runtimes; + + CheckboxTableViewer runtimesViewer; + + Text globalSettingsText; + + private String globalSettings; + + boolean dirty = false; + + public MavenInstallationsPreferencePage() { + setTitle(Messages.MavenInstallationsPreferencePage_title); + + this.mavenPlugin = MavenPlugin.getDefault(); + this.runtimeManager = mavenPlugin.getMavenRuntimeManager(); + this.mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + this.maven = MavenPlugin.getDefault().getMaven(); + + } + + public void init(IWorkbench workbench) { + } + + protected void performDefaults() { + runtimeManager.reset(); + defaultRuntime = runtimeManager.getDefaultRuntime(); + runtimes = runtimeManager.getMavenRuntimes(); + + runtimesViewer.setInput(runtimes); + runtimesViewer.setChecked(defaultRuntime, true); + runtimesViewer.refresh(); + + storeCustom(""); //$NON-NLS-1$ + globalSettingsText.setText(""); //$NON-NLS-1$ + mavenConfiguration.setGlobalSettingsFile(""); //$NON-NLS-1$ + + updateGlobals(true); + super.performDefaults(); + setDirty(true); + } + + + protected void storeCustom(String dir){ + mavenPlugin.getPreferenceStore().setValue(P_MAVEN_CUSTOM_GLOBAL, dir == null ? "" : dir); //$NON-NLS-1$ + } + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#performApply() + */ + protected void performApply() { + updateSettings(); + } + + public void updateSettings(){ + new Job(Messages.MavenInstallationsPreferencePage_job_updating) { + protected IStatus run(IProgressMonitor monitor) { + String dir = getGlobalSettingsText(); + + runtimeManager.setRuntimes(runtimes); + runtimeManager.setDefaultRuntime(defaultRuntime); + String oldSettings = mavenConfiguration.getGlobalSettingsFile(); + + mavenConfiguration.setGlobalSettingsFile(dir); + if(defaultRuntime == null || defaultRuntime instanceof MavenEmbeddedRuntime){ + storeCustom(dir); + } + IndexManager indexManager = mavenPlugin.getIndexManager(); + try { + indexManager.getWorkspaceIndex().updateIndex(true, monitor); + } catch(CoreException ex) { + return ex.getStatus(); + } + if((dir == null && oldSettings != null) || (dir != null && !(dir.equals(oldSettings)))){ + //mavenPlugin.getIndexManager().scheduleIndexUpdate(IndexManager.LOCAL_INDEX, true, 0L); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public boolean performOk() { + if (dirty) { + updateSettings(); + } + return true; + } + + public void setDirty(boolean dirty){ + this.dirty = dirty; + } + + public boolean isDirty(){ + return this.dirty; + } + + protected boolean validateMavenInstall(String dir){ + if(dir == null || dir.length() == 0){ + return false; + } + File selectedDir = new File(dir); + if(!selectedDir.isDirectory()){ + MessageDialog.openError(getShell(), Messages.MavenInstallationsPreferencePage_error_title, Messages.MavenInstallationsPreferencePage_error_message); + return false; + } + File binDir = new File(dir, "bin"); //$NON-NLS-1$ + File confDir = new File(dir, "conf"); //$NON-NLS-1$ + File libDir = new File(dir, "lib"); //$NON-NLS-1$ + if(!binDir.exists() || !confDir.exists() || !libDir.exists()){ + MessageDialog.openError(getShell(), Messages.MavenInstallationsPreferencePage_error_title, Messages.MavenInstallationsPreferencePage_error2_message); + return false; + } + return true; + } + protected Control createContents(Composite parent) { + + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(3, false); + gridLayout.marginBottom = 5; + gridLayout.marginRight = 5; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + composite.setLayout(gridLayout); + + Label link = new Label(composite, SWT.NONE); + link.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + link.setText(Messages.MavenInstallationsPreferencePage_link); + + + createTable(composite); + createGlobalSettings(composite); + + + defaultRuntime = runtimeManager.getDefaultRuntime(); + runtimes = runtimeManager.getMavenRuntimes(); + + runtimesViewer.setInput(runtimes); + runtimesViewer.setChecked(defaultRuntime, true); + runtimesViewer.refresh(); // should listen on property changes instead? + + checkSettings(); + updateGlobals(false); + globalSettingsText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent modifyevent) { + setGlobalSettingsText(globalSettingsText.getText()); + updateGlobalSettingsLink(); + checkSettings(); + setDirty(true); + } + }); + + return composite; + } + + /** + * + */ + private void updateGlobalSettingsText(boolean useLastCustomGlobal) { + String globalSettings = getGlobalSettingsFile(useLastCustomGlobal); + globalSettingsText.setText(globalSettings == null ? "" : globalSettings); //$NON-NLS-1$ + } + + /** + * Use this to retrieve the global settings file which has not been applied yet + * @return + */ + public String getGlobalSettingsFile(boolean useLastCustomGlobal) { + if(defaultRuntime == null || defaultRuntime instanceof MavenEmbeddedRuntime){ + String globalSettings = null; + if(useLastCustomGlobal){ + globalSettings = mavenPlugin.getPreferenceStore().getString(P_MAVEN_CUSTOM_GLOBAL); + } else { + globalSettings = mavenPlugin.getPreferenceStore().getString(MavenPreferenceConstants.P_GLOBAL_SETTINGS_FILE); + } + return globalSettings.trim().length()==0 ? null : globalSettings; + } + return defaultRuntime == null ? null : defaultRuntime.getSettings(); + } + + public void setGlobalSettingsText(String settings){ + this.globalSettings = settings; + } + public String getGlobalSettingsText(){ + return this.globalSettings; + } + /** + * + */ + private void updateGlobals(boolean useLastCustomGlobal) { + updateGlobalSettingsText(useLastCustomGlobal); + updateGlobalSettingsLink(); + updateGlobalSettingsBrowseButton(); + } + + private Link globalSettingsLink; + + private Button globalSettingsBrowseButton; + + private MavenRuntime getCheckedRuntime(){ + Object[] runtimes = runtimesViewer.getCheckedElements(); + if(runtimes != null && runtimes.length > 0){ + return (MavenRuntime)runtimes[0]; + } + return null; + } + + protected MavenRuntime getSelectedMavenRuntime(){ + IStructuredSelection sel = (IStructuredSelection)runtimesViewer.getSelection(); + return (MavenRuntime) sel.getFirstElement(); + } + + private void updateGlobalSettingsLink(){ + MavenRuntime runtime = getCheckedRuntime(); + String text = ""; //$NON-NLS-1$ + String currText = globalSettingsText.getText(); + boolean showURL = false; + + File f = new File(currText); + if(f.exists()){ + showURL = true; + } + String openFile = showURL ? Messages.MavenInstallationsPreferencePage_link_open : ":"; //$NON-NLS-2$ + if(runtime instanceof MavenEmbeddedRuntime){ + text = Messages.MavenInstallationsPreferencePage_settings+openFile; + } else { + text = Messages.MavenInstallationsPreferencePage_settings_install+openFile; + } + globalSettingsLink.setText(text); + } + + private void updateGlobalSettingsBrowseButton(){ + MavenRuntime runtime = getCheckedRuntime(); + boolean enabled = (runtime != null && (runtime instanceof MavenEmbeddedRuntime)); + globalSettingsBrowseButton.setEnabled(enabled); + globalSettingsText.setEditable(enabled); + } + + private void createGlobalSettings(Composite composite) { + globalSettingsLink = new Link(composite, SWT.NONE); + globalSettingsLink.setData("name", "globalSettingsLink"); //$NON-NLS-1$ //$NON-NLS-2$ + + globalSettingsLink.setToolTipText(Messages.MavenInstallationsPreferencePage_link_global); + GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); + gd.verticalIndent = 25; + globalSettingsLink.setLayoutData(gd); + + globalSettingsLink.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + String globalSettings = getGlobalSettings(); + if(globalSettings.length() == 0) { + globalSettings = defaultRuntime.getSettings(); + } + if(globalSettings != null && globalSettings.length() > 0) { + openEditor(globalSettings); + } + } + }); + + globalSettingsText = new Text(composite, SWT.BORDER); + globalSettingsText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + globalSettingsText.setData("name", "globalSettingsText"); //$NON-NLS-1$ //$NON-NLS-2$ + globalSettingsBrowseButton = new Button(composite, SWT.NONE); + GridData gd_globalSettingsBrowseButton = new GridData(SWT.FILL, SWT.CENTER, false, false); + globalSettingsBrowseButton.setLayoutData(gd_globalSettingsBrowseButton); + globalSettingsBrowseButton.setText(Messages.MavenInstallationsPreferencePage_btnGlobalBrowse); + globalSettingsBrowseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + if(getGlobalSettings().length() > 0) { + dialog.setFileName(getGlobalSettings()); + } + String file = dialog.open(); + if(file != null) { + file = file.trim(); + if(file.length() > 0) { + globalSettingsText.setText(file); + } + } + } + }); + } + + + private void createTable(Composite composite){ + runtimesViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER | SWT.FULL_SELECTION); + + runtimesViewer.setLabelProvider(new RuntimesLabelProvider()); + + runtimesViewer.setContentProvider(new IStructuredContentProvider() { + + @SuppressWarnings("unchecked") + public Object[] getElements(Object input) { + if(input instanceof List) { + List list = (List) input; + if(list.size() > 0) { + return list.toArray(new MavenRuntime[list.size()]); + } + } + return new Object[0]; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + + }); + + Table table = runtimesViewer.getTable(); + table.setLinesVisible(false); + table.setHeaderVisible(false); + GridData gd_table = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 3); + gd_table.heightHint = 151; + gd_table.widthHint = 333; + table.setLayoutData(gd_table); + + TableColumn typeColumn = new TableColumn(table, SWT.NONE); + typeColumn.setWidth(325); + typeColumn.setText(""); //$NON-NLS-1$ + + Button addButton = new Button(composite, SWT.NONE); + addButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + addButton.setText(Messages.MavenInstallationsPreferencePage_btnAdd); + addButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + DirectoryDialog dlg = new DirectoryDialog(getShell()); + dlg.setText(Messages.MavenInstallationsPreferencePage_dialog_install_title); + dlg.setMessage(Messages.MavenInstallationsPreferencePage_dialog_install_message); + String dir = dlg.open(); + if(dir == null){ + return; + } + boolean ok = validateMavenInstall(dir); + if(ok){ + MavenRuntime runtime = MavenRuntimeManager.createExternalRuntime(dir); + if(runtimes.contains(runtime)) { + MessageDialog.openError(getShell(), Messages.MavenInstallationsPreferencePage_error_title, Messages.MavenInstallationsPreferencePage_error3_message); + } else { + runtimes.add(runtime); + runtimesViewer.refresh(); + runtimesViewer.setAllChecked(false); + runtimesViewer.setChecked(runtime, true); + if(runtime != null){ + setCheckedRuntime(runtime); + } + } + } + } + }); + + final Button editButton = new Button(composite, SWT.NONE); + editButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + editButton.setEnabled(false); + editButton.setText(Messages.MavenInstallationsPreferencePage_btnEdit); + editButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + MavenRuntime runtime = getSelectedMavenRuntime(); + DirectoryDialog dlg = new DirectoryDialog(Display.getCurrent().getActiveShell()); + dlg.setText(Messages.MavenInstallationsPreferencePage_dialog_title); + dlg.setMessage(Messages.MavenInstallationsPreferencePage_dialog_message); + dlg.setFilterPath(runtime.getLocation()); + String dir = dlg.open(); + boolean ok = validateMavenInstall(dir); + if(ok && !dir.equals(runtime.getLocation())) { + MavenRuntime newRuntime = MavenRuntimeManager.createExternalRuntime(dir); + if(runtimes.contains(newRuntime)) { + MessageDialog.openError(getShell(), Messages.MavenInstallationsPreferencePage_error_title, Messages.MavenInstallationsPreferencePage_error4_message); + } else { + runtimes.set(runtimes.indexOf(runtime), newRuntime); + runtimesViewer.refresh(); + setDirty(true); + if(newRuntime != null){ + setCheckedRuntime(newRuntime); + } + } + } + } + }); + + final Button removeButton = new Button(composite, SWT.NONE); + removeButton.setEnabled(false); + removeButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + removeButton.setText(Messages.MavenInstallationsPreferencePage_btnRemove); + removeButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + MavenRuntime runtime = getSelectedMavenRuntime(); + runtimes.remove(runtime); + runtimesViewer.refresh(); + Object[] checkedElements = runtimesViewer.getCheckedElements(); + if(checkedElements == null || checkedElements.length == 0) { + defaultRuntime = runtimeManager.getRuntime(MavenRuntimeManager.EMBEDDED); + runtimesViewer.setChecked(defaultRuntime, true); + setCheckedRuntime(defaultRuntime); + } + setDirty(true); + } + }); + + runtimesViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if(runtimesViewer.getSelection() instanceof IStructuredSelection) { + MavenRuntime runtime = getSelectedMavenRuntime(); + boolean isEnabled = runtime != null && runtime.isEditable(); + removeButton.setEnabled(isEnabled); + editButton.setEnabled(isEnabled); + } + } + }); + + runtimesViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + if(event.getElement() != null && event.getChecked()){ + + setCheckedRuntime((MavenRuntime)event.getElement()); + } + } + }); + Link noteLabel = new Link(composite, SWT.WRAP | SWT.READ_ONLY); + GridData noteLabelData = new GridData(SWT.FILL, SWT.TOP, false, false, 2, 1); + noteLabelData.horizontalIndent = 15; + noteLabelData.widthHint = 100; + + noteLabel.setLayoutData(noteLabelData); + noteLabel.setText(Messages.MavenInstallationsPreferencePage_lblNote1 + + Messages.MavenInstallationsPreferencePage_lblNote2); + noteLabel.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + try { + URL url = new URL(e.text); + IWebBrowser browser = PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser(); + browser.openURL(url); + } catch(MalformedURLException ex) { + MavenLogger.log("Malformed URL", ex); + } catch(PartInitException ex) { + MavenLogger.log(ex); + } + } + }); + } + + private static final String P_MAVEN_CUSTOM_GLOBAL = "customGlobalSettingsFile"; //$NON-NLS-1$ + + protected void setCheckedRuntime(MavenRuntime runtime){ + runtimesViewer.setAllChecked(false); + runtimesViewer.setChecked(runtime, true); + defaultRuntime = runtime; + boolean useDefault = (defaultRuntime == null || defaultRuntime instanceof MavenEmbeddedRuntime); + updateGlobals(useDefault); + setDirty(true); + } + + void checkSettings() { + setErrorMessage(null); + setMessage(null); + + String globalSettings = getGlobalSettings(); + if(globalSettings != null && globalSettings.length() > 0) { + File globalSettingsFile = new File(globalSettings); + if(!globalSettingsFile.exists()) { + setMessage(Messages.MavenInstallationsPreferencePage_error_global_missing, IMessageProvider.WARNING); + globalSettings = null; + } + } else { + globalSettings = null; + } + + List<SettingsProblem> result = maven.validateSettings(globalSettings); + if(result.size() > 0) { + setMessage(Messages.MavenInstallationsPreferencePage_error_global_parse + result.get(0).getMessage(), IMessageProvider.WARNING); + } + + } + + + + @SuppressWarnings("unchecked") + void openEditor(final String fileName) { + // XXX create new settings.xml if does not exist + + IWorkbench workbench = PlatformUI.getWorkbench(); + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + IWorkbenchPage page = window.getActivePage(); + + IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor("settings.xml"); //$NON-NLS-1$ + + File file = new File(fileName); + IEditorInput input = null; + try { + //class implementing editor input for external file has been renamed in eclipse 3.3, hence reflection + Class javaInput = null; + try { + javaInput = Class.forName("org.eclipse.ui.internal.editors.text.JavaFileEditorInput"); //$NON-NLS-1$ + Constructor cons = javaInput.getConstructor(new Class[] {File.class}); + input = (IEditorInput) cons.newInstance(new Object[] {file}); + } catch(Exception e) { + try { + IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(file); + Class storeInput = Class.forName("org.eclipse.ui.ide.FileStoreEditorInput"); //$NON-NLS-1$ + Constructor cons = storeInput.getConstructor(new Class[] {IFileStore.class}); + input = (IEditorInput) cons.newInstance(new Object[] {fileStore}); + } catch(Exception ex) { + //ignore... + } + } + final IEditorPart editor = IDE.openEditor(page, input, desc.getId()); + editor.addPropertyListener(new IPropertyListener() { + public void propertyChanged(Object source, int propId) { + if(!editor.isDirty()) { + mavenPlugin.getConsole().logMessage("Refreshing settings " + fileName); + invalidateMavenSettings(false); + } + } + }); + + } catch(PartInitException ex) { + MavenLogger.log(ex); + } + } + + + void invalidateMavenSettings(final boolean reindex) { +// new Job("Invalidating Maven settings") { +// protected IStatus run(IProgressMonitor monitor) { +// mavenPlugin.getMavenEmbedderManager().invalidateMavenSettings(); +// if(reindex) { +// mavenPlugin.getIndexManager().scheduleIndexUpdate(IndexManager.LOCAL_INDEX, true, 0L); +// } +// return Status.OK_STATUS; +// } +// }.schedule(); + } + String getGlobalSettings() { + return globalSettingsText.getText().trim(); + } + + static class RuntimesLabelProvider implements ITableLabelProvider, IColorProvider { + + public String getColumnText(Object element, int columnIndex) { + MavenRuntime runtime = (MavenRuntime) element; + return runtime.toString(); + } + + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + public Color getBackground(Object element) { + return null; + } + + public Color getForeground(Object element) { + MavenRuntime runtime = (MavenRuntime) element; + if(!runtime.isEditable()) { + return Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY); + } + return null; + } + + public void dispose() { + } + + public boolean isLabelProperty(Object element, String property) { + return false; + } + + public void addListener(ILabelProviderListener listener) { + } + + public void removeListener(ILabelProviderListener listener) { + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenPreferencePage.java new file mode 100644 index 00000000..18105440 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenPreferencePage.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +public class MavenPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + final MavenPlugin plugin; + + public MavenPreferencePage() { + super(GRID); + setPreferenceStore(MavenPlugin.getDefault().getPreferenceStore()); + + plugin = MavenPlugin.getDefault(); + } + + public void init(IWorkbench workbench) { + } + + /* + * Creates the field editors. Field editors are abstractions of the common GUI + * blocks needed to manipulate various types of preferences. Each field editor + * knows how to save and restore itself. + */ + public void createFieldEditors() { + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_OFFLINE, Messages.getString("preferences.offline"), //$NON-NLS-1$ + getFieldEditorParent())); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_DEBUG_OUTPUT, // + Messages.getString("preferences.debugOutput"), //$NON-NLS-1$ + getFieldEditorParent())); + + // addField( new BooleanFieldEditor( MavenPreferenceConstants.P_UPDATE_SNAPSHOTS, + // Messages.getString( "preferences.updateSnapshots" ), //$NON-NLS-1$ + // getFieldEditorParent() ) ); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_DOWNLOAD_SOURCES, // + Messages.getString("preferences.downloadSources"), //$NON-NLS-1$ + getFieldEditorParent())); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_DOWNLOAD_JAVADOC, // + Messages.getString("preferences.downloadJavadoc"), //$NON-NLS-1$ + getFieldEditorParent())); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_UPDATE_INDEXES, // + org.eclipse.m2e.core.internal.Messages.MavenPreferencePage_download, // + getFieldEditorParent())); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_UPDATE_PROJECTS, // + org.eclipse.m2e.core.internal.Messages.MavenPreferencePage_update, // + getFieldEditorParent())); + + addField(new BooleanFieldEditor(MavenPreferenceConstants.P_HIDE_FOLDERS_OF_NESTED_PROJECTS, // + org.eclipse.m2e.core.internal.Messages.MavenPreferencePage_hide, getFieldEditorParent())); + + GridData comboCompositeGridData = new GridData(); + comboCompositeGridData.verticalIndent = 25; + comboCompositeGridData.horizontalSpan = 3; + comboCompositeGridData.grabExcessHorizontalSpace = true; + comboCompositeGridData.horizontalAlignment = GridData.FILL; + + Composite comboComposite = new Composite(getFieldEditorParent(), SWT.NONE); + comboComposite.setLayoutData(comboCompositeGridData); + comboComposite.setLayout(new GridLayout(2, false)); + + // addSeparator(); + } + + private void addSeparator() { + Label separator = new Label(getFieldEditorParent(), SWT.HORIZONTAL | SWT.SEPARATOR); + // separator.setVisible(false); + GridData separatorGridData = new GridData(); + separatorGridData.horizontalSpan = 4; + separatorGridData.grabExcessHorizontalSpace = true; + separatorGridData.horizontalAlignment = GridData.FILL; + separatorGridData.verticalIndent = 10; + separator.setLayoutData(separatorGridData); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectLifecycleMappingPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectLifecycleMappingPage.java new file mode 100644 index 00000000..fca8e44d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectLifecycleMappingPage.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.dialogs.PropertyPage; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.ui.internal.lifecycle.ILifecyclePropertyPage; +import org.eclipse.m2e.core.ui.internal.lifecycle.LifecycleMappingPropertyPageFactory; + +/** + * Maven project preference page + * + * @author Dan Yocum + */ +public class MavenProjectLifecycleMappingPage extends PropertyPage{ + + private ILifecyclePropertyPage currentPage; + + public MavenProjectLifecycleMappingPage() { + + setTitle(""); //$NON-NLS-1$ + } + + protected Control createContents(Composite parent) { + currentPage = loadCurrentPage((IProject)getElement()); + setMessage(currentPage.getName()); + return currentPage.createContents(parent); + } + + private ILifecyclePropertyPage getErrorPage(String msg){ + SimpleLifecycleMappingPropertyPage p = new SimpleLifecycleMappingPropertyPage(msg); + return p; + } + + private ILifecyclePropertyPage getPage(ILifecycleMapping lifecycleMapping){ + ILifecyclePropertyPage page = LifecycleMappingPropertyPageFactory.getFactory().getPageForId(lifecycleMapping.getId(), getProject(), this.getShell()); + if(page == null){ + page = getErrorPage(Messages.MavenProjectLifecycleMappingPage_error_no_page); + page.setName(lifecycleMapping.getName()); + } + return page; + } + + private ILifecyclePropertyPage loadCurrentPage(IProject project){ + ILifecyclePropertyPage page = null; + try{ + ILifecycleMapping lifecycleMapping = LifecycleMappingPropertyPageFactory.getLifecycleMapping(project); + if(lifecycleMapping == null){ + return getErrorPage(Messages.MavenProjectLifecycleMappingPage_error_no_strategy); + } + page = getPage(lifecycleMapping); + return page; + } catch(CoreException ce){ + MavenLogger.log(ce); + SimpleLifecycleMappingPropertyPage p = new SimpleLifecycleMappingPropertyPage(Messages.MavenProjectLifecycleMappingPage_error_page_error); + return p; + } + } + + protected void performDefaults() { + currentPage.performDefaults(); + } + + protected IProject getProject() { + return (IProject) getElement(); + } + + public boolean performOk() { + return currentPage.performOk(); + } + + public void setElement(IAdaptable element){ + if(currentPage != null && element instanceof IProject){ + currentPage.setProject((IProject)element); + } + super.setElement(element); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectPreferencePage.java new file mode 100644 index 00000000..b6078c36 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenProjectPreferencePage.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.PropertyPage; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.core.project.ResolverConfiguration; + +/** + * Maven project preference page + * + * @author Eugene Kuleshov + */ +public class MavenProjectPreferencePage extends PropertyPage { + + private Button resolveWorspaceProjectsButton; +// private Button includeModulesButton; + + private Text activeProfilesText; + + public MavenProjectPreferencePage() { + setTitle(Messages.MavenProjectPreferencePage_title); + } + + protected Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(GridData.FILL)); + + Label profilesLabel = new Label(composite, SWT.NONE); + profilesLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + profilesLabel.setText(Messages.MavenProjectPreferencePage_lblProfiles); + + activeProfilesText = new Text(composite, SWT.BORDER); + activeProfilesText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + resolveWorspaceProjectsButton = new Button(composite, SWT.CHECK); + GridData resolveWorspaceProjectsButtonData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); + resolveWorspaceProjectsButton.setLayoutData(resolveWorspaceProjectsButtonData); + resolveWorspaceProjectsButton.setText(Messages.MavenProjectPreferencePage_btnResolve); + +// includeModulesButton = new Button(composite, SWT.CHECK); +// GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1); +// gd.verticalIndent = 15; +// includeModulesButton.setLayoutData(gd); +// includeModulesButton.setText("Include &Modules"); +// +// Text includeModulesText = new Text(composite, SWT.WRAP | SWT.READ_ONLY | SWT.MULTI); +// includeModulesText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE)); +// GridData gd_includeModulesText = new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1); +// gd_includeModulesText.horizontalIndent = 15; +// gd_includeModulesText.verticalIndent = 0; +// gd_includeModulesText.widthHint = 300; +// gd_includeModulesText.heightHint = 120; +// includeModulesText.setLayoutData(gd_includeModulesText); +// includeModulesText.setBackground(composite.getBackground()); +// includeModulesText.setText("When enabled, dependencies from all nested modules " +// + "are added to the \"Maven Dependencies\" container and " +// + "source folders from nested modules are added to the current " +// + "project build path (use \"Update Sources\" action)"); + + init(getResolverConfiguration()); + + return composite; + } + + protected void performDefaults() { + init(new ResolverConfiguration()); + } + + private void init(ResolverConfiguration configuration) { + + resolveWorspaceProjectsButton.setSelection(configuration.shouldResolveWorkspaceProjects()); +// includeModulesButton.setSelection(configuration.shouldIncludeModules()); + activeProfilesText.setText(configuration.getActiveProfiles()); + } + + public boolean performOk() { + final IProject project = getProject(); + try { + if(!project.isAccessible() || !project.hasNature(IMavenConstants.NATURE_ID)) { + return true; + } + } catch(CoreException ex) { + MavenLogger.log(ex); + return false; + } + + final ResolverConfiguration configuration = getResolverConfiguration(); + if(configuration.getActiveProfiles().equals(activeProfilesText.getText()) && +// configuration.shouldIncludeModules()==includeModulesButton.getSelection() && + configuration.shouldResolveWorkspaceProjects()==resolveWorspaceProjectsButton.getSelection()) { + return true; + } + + configuration.setResolveWorkspaceProjects(resolveWorspaceProjectsButton.getSelection()); +// configuration.setIncludeModules(includeModulesButton.getSelection()); + configuration.setActiveProfiles(activeProfilesText.getText()); + + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + boolean isSet = projectManager.setResolverConfiguration(getProject(), configuration); + if(isSet) { + + boolean res = MessageDialog.openQuestion(getShell(), Messages.MavenProjectPreferencePage_dialog_title, // + Messages.MavenProjectPreferencePage_dialog_message); + if(res) { + final MavenPlugin plugin = MavenPlugin.getDefault(); + WorkspaceJob job = new WorkspaceJob(NLS.bind(Messages.MavenProjectPreferencePage_job, project.getName() )) { + public IStatus runInWorkspace(IProgressMonitor monitor) { + try { + plugin.getProjectConfigurationManager().updateProjectConfiguration(project, configuration, monitor); + } catch(CoreException ex) { + return ex.getStatus(); + } + return Status.OK_STATUS; + } + }; + job.setRule(plugin.getProjectConfigurationManager().getRule()); + job.schedule(); + } + + } + + return isSet; + } + + private ResolverConfiguration getResolverConfiguration() { + MavenProjectManager projectManager = MavenPlugin.getDefault().getMavenProjectManager(); + return projectManager.getResolverConfiguration(getProject()); + } + + private IProject getProject() { + return (IProject) getElement(); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenSettingsPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenSettingsPreferencePage.java new file mode 100644 index 00000000..788b8726 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MavenSettingsPreferencePage.java @@ -0,0 +1,413 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IPropertyListener; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; + +import org.apache.maven.cli.MavenCli; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.building.SettingsProblem; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.embedder.IMavenConfiguration; +import org.eclipse.m2e.core.embedder.MavenRuntime; +import org.eclipse.m2e.core.embedder.MavenRuntimeManager; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenUpdateRequest; + + +/** + * Maven installations preference page + * + * @author Eugene Kuleshov + */ +public class MavenSettingsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + final MavenPlugin mavenPlugin; + + final MavenRuntimeManager runtimeManager; + + final IMavenConfiguration mavenConfiguration; + + final IMaven maven; + + MavenRuntime defaultRuntime; + + + Text userSettingsText; + + Text localRepositoryText; + + boolean dirty = false; + + private Link userSettingsLink; + + public MavenSettingsPreferencePage() { + setTitle(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_title); + + this.mavenPlugin = MavenPlugin.getDefault(); + this.runtimeManager = mavenPlugin.getMavenRuntimeManager(); + this.mavenConfiguration = MavenPlugin.getDefault().getMavenConfiguration(); + this.maven = MavenPlugin.getDefault().getMaven(); + } + + public void init(IWorkbench workbench) { + } + + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean) + */ + public void setVisible(boolean visible) { + super.setVisible(visible); + if(visible){ + updateLocalRepository(); + } + } + + protected void performDefaults() { + userSettingsText.setText(MavenCli.DEFAULT_USER_SETTINGS_FILE.getAbsolutePath()); + setDirty(true); + updateLocalRepository(); + super.performDefaults(); + } + + protected void updateSettings(final boolean updateMavenDependencies){ + final String userSettings = getUserSettings(); + + new Job(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_job_updating) { + protected IStatus run(IProgressMonitor monitor) { + try { + final File localRepositoryDir = new File(maven.getLocalRepository().getBasedir()); + if(userSettings.length() > 0) { + mavenConfiguration.setUserSettingsFile(userSettings); + } else { + mavenConfiguration.setUserSettingsFile(null); + } + + File newRepositoryDir = new File(maven.getLocalRepository().getBasedir()); + if(!newRepositoryDir.equals(localRepositoryDir)) { + IndexManager indexManager = mavenPlugin.getIndexManager(); + indexManager.getWorkspaceIndex().updateIndex(true, monitor); + } + if(updateMavenDependencies){ + IMavenProjectFacade[] projects = MavenPlugin.getDefault().getMavenProjectManager().getProjects(); + ArrayList<IProject> allProjects = new ArrayList<IProject>(); + if(projects != null){ + MavenPlugin.getDefault().getMaven().reloadSettings(); + SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, projects.length); + for(int i=0;i<projects.length;i++){ + subMonitor.beginTask(NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_task_updating, projects[i].getProject().getName()), 1); + allProjects.add(projects[i].getProject()); + } + IMavenConfiguration configuration = MavenPlugin.getDefault().getMavenConfiguration(); + MavenPlugin.getDefault().getMavenProjectManager().refresh(new MavenUpdateRequest(allProjects.toArray(new IProject[]{}), configuration.isOffline(), true)); + subMonitor.done(); + } + } + return Status.OK_STATUS; + } catch (CoreException e) { + return e.getStatus(); + } + } + }.schedule(); + } + + protected void performApply() { + if(dirty){ + updateSettings(false); + } + } + + public boolean performOk() { + if (dirty) { + updateSettings(false); + } + return true; + } + + public void setDirty(boolean dirty){ + this.dirty = dirty; + } + + public boolean isDirty(){ + return this.dirty; + } + + protected Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(4, false); + gridLayout.marginBottom = 5; + gridLayout.marginRight = 5; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + composite.setLayout(gridLayout); + + createUserSettings(composite); + Label localRepositoryLabel = new Label(composite, SWT.NONE); + GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1); + gd.verticalIndent=25; + localRepositoryLabel.setLayoutData(gd); + localRepositoryLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_lblLocal); + + localRepositoryText = new Text(composite, SWT.READ_ONLY|SWT.BORDER); + localRepositoryText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); + localRepositoryText.setData("name", "localRepositoryText"); //$NON-NLS-1$ //$NON-NLS-2$ + localRepositoryText.setEditable(false); + Button reindexButton = new Button(composite, SWT.NONE); + reindexButton.setLayoutData(new GridData(SWT.FILL, SWT.RIGHT, false, false, 1, 1)); + reindexButton.setText(Messages.getString("preferences.reindexButton")); //$NON-NLS-1$ + reindexButton.addSelectionListener(new SelectionAdapter(){ + + /* (non-Javadoc) + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + public void widgetSelected(SelectionEvent e) { + new WorkspaceJob(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_job_indexing) { + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + IndexManager indexManager = mavenPlugin.getIndexManager(); + indexManager.getWorkspaceIndex().updateIndex(true, monitor); + return Status.OK_STATUS; + } + }.schedule(); + } + }); + defaultRuntime = runtimeManager.getDefaultRuntime(); + + String userSettings = mavenConfiguration.getUserSettingsFile(); + if(userSettings == null || userSettings.length() == 0) { + userSettingsText.setText(MavenCli.DEFAULT_USER_SETTINGS_FILE.getAbsolutePath()); + } else { + userSettingsText.setText(userSettings); + } + + checkSettings(); + updateLocalRepository(); + + userSettingsText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent modifyevent) { + updateLocalRepository(); + checkSettings(); + setDirty(true); + } + }); + + return composite; + } + + public void updateSettingsLink(boolean active){ + String text = org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_link1; + if(active){ + text = org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_link2; + } + userSettingsLink.setText(text); + } + /** + * @param composite + */ + private void createUserSettings(Composite composite) { + + userSettingsLink = new Link(composite, SWT.NONE); + userSettingsLink.setData("name", "userSettingsLink"); //$NON-NLS-1$ //$NON-NLS-2$ + userSettingsLink.setText(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_link2); + userSettingsLink.setToolTipText(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_link_tooltip); + GridData gd_userSettingsLabel = new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1); + + gd_userSettingsLabel.verticalIndent = 15; + userSettingsLink.setLayoutData(gd_userSettingsLabel); + userSettingsLink.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + String userSettings = getUserSettings(); + if(userSettings.length() == 0) { + userSettings = MavenCli.DEFAULT_USER_SETTINGS_FILE.getAbsolutePath(); + } + openEditor(userSettings); + } + }); + userSettingsText = new Text(composite, SWT.BORDER); + userSettingsText.setData("name", "userSettingsText"); //$NON-NLS-1$ //$NON-NLS-2$ + GridData gd_userSettingsText = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); + gd_userSettingsText.verticalIndent = 5; + gd_userSettingsText.widthHint = 100; + userSettingsText.setLayoutData(gd_userSettingsText); + + Button userSettingsBrowseButton = new Button(composite, SWT.NONE); + GridData gd_userSettingsBrowseButton = new GridData(SWT.FILL, SWT.RIGHT, false, false, 1, 1); + + userSettingsBrowseButton.setLayoutData(gd_userSettingsBrowseButton); + userSettingsBrowseButton.setText(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_btnBrowse); + userSettingsBrowseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + if(getUserSettings().length() > 0) { + dialog.setFileName(getUserSettings()); + } + String file = dialog.open(); + if(file != null) { + file = file.trim(); + if(file.length() > 0) { + userSettingsText.setText(file); + updateLocalRepository(); + checkSettings(); + } + } + } + }); + + Button updateSettings = new Button(composite, SWT.NONE); + GridData gd = new GridData(SWT.FILL, SWT.LEFT, false, false, 1, 1); + updateSettings.setText(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_btnUpdate); + updateSettings.addSelectionListener(new SelectionAdapter(){ + public void widgetSelected(SelectionEvent e){ + updateSettings(true); + } + }); + } + + protected void updateLocalRepository() { + final String userSettings = getUserSettings(); + String globalSettings = runtimeManager.getGlobalSettingsFile(); + try { + Settings settings = maven.buildSettings(globalSettings, userSettings); + String localRepository = settings.getLocalRepository(); + if(localRepository == null){ + localRepository = RepositorySystem.defaultUserLocalRepository.getAbsolutePath(); + } + if(!localRepositoryText.isDisposed()) { + localRepositoryText.setText(localRepository == null ? "" : localRepository); //$NON-NLS-1$ + } + } catch (CoreException e) { + setMessage(e.getMessage(), IMessageProvider.ERROR); + } + } + + protected void checkSettings() { + setErrorMessage(null); + setMessage(null); + boolean fileExists = false; + String userSettings = getUserSettings(); + if(userSettings != null && userSettings.length() > 0) { + File userSettingsFile = new File(userSettings); + if(!userSettingsFile.exists()) { + setMessage(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_error_missing, IMessageProvider.WARNING); + userSettings = null; + + } else { + fileExists = true; + } + + } else { + userSettings = null; + } + updateSettingsLink(fileExists); + List<SettingsProblem> result = maven.validateSettings(userSettings); + if(result.size() > 0) { + setMessage(NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenSettingsPreferencePage_error_parse, result.get(0).getMessage()), IMessageProvider.WARNING); + } + } + + + + @SuppressWarnings("unchecked") + void openEditor(final String fileName) { + IWorkbench workbench = PlatformUI.getWorkbench(); + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + IWorkbenchPage page = window.getActivePage(); + + IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor("settings.xml"); //$NON-NLS-1$ + + File file = new File(fileName); + IEditorInput input = null; + try { + //class implementing editor input for external file has been renamed in eclipse 3.3, hence reflection + Class javaInput = null; + try { + javaInput = Class.forName("org.eclipse.ui.internal.editors.text.JavaFileEditorInput"); //$NON-NLS-1$ + Constructor cons = javaInput.getConstructor(new Class[] {File.class}); + input = (IEditorInput) cons.newInstance(new Object[] {file}); + } catch(Exception e) { + try { + IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(file); + Class storeInput = Class.forName("org.eclipse.ui.ide.FileStoreEditorInput"); //$NON-NLS-1$ + Constructor cons = storeInput.getConstructor(new Class[] {IFileStore.class}); + input = (IEditorInput) cons.newInstance(new Object[] {fileStore}); + } catch(Exception ex) { + //ignore... + } + } + final IEditorPart editor = IDE.openEditor(page, input, desc.getId()); + editor.addPropertyListener(new IPropertyListener() { + public void propertyChanged(Object source, int propId) { + if(!editor.isDirty()) { + mavenPlugin.getConsole().logMessage("Refreshing settings " + fileName); + } + } + }); + + } catch(PartInitException ex) { + MavenLogger.log(ex); + } + } + + String getUserSettings() { + return userSettingsText.getText().trim(); + } + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MissingLifecycleMappingPropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MissingLifecycleMappingPropertyPage.java new file mode 100644 index 00000000..602e7322 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/MissingLifecycleMappingPropertyPage.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.project.MissingLifecycleMapping; +import org.eclipse.m2e.core.project.configurator.ILifecycleMapping; +import org.eclipse.m2e.core.ui.internal.lifecycle.LifecycleMappingPropertyPageFactory; + +public class MissingLifecycleMappingPropertyPage extends SimpleLifecycleMappingPropertyPage { + + public MissingLifecycleMappingPropertyPage() { + super(Messages.MissingLifecycleMappingPropertyPage_title); + } + + protected String getMessage() { + try { + ILifecycleMapping lifecycleMapping = LifecycleMappingPropertyPageFactory.getLifecycleMapping(getProject()); + if (lifecycleMapping instanceof MissingLifecycleMapping) { + String missingId = ((MissingLifecycleMapping) lifecycleMapping).getMissingMappingId(); + return NLS.bind(Messages.MissingLifecycleMappingPropertyPage_error, missingId); + } + } catch(CoreException ex) { + // this is odd, but lets ignore it anyways + } + return super.getMessage(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/ProblemReportingPreferencePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/ProblemReportingPreferencePage.java new file mode 100644 index 00000000..90abeaf9 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/ProblemReportingPreferencePage.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.internal.preferences.MavenPreferenceConstants; + + +public class ProblemReportingPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + final MavenPlugin plugin; + private Composite parent; + + public ProblemReportingPreferencePage() { + super(GRID); + setPreferenceStore(MavenPlugin.getDefault().getPreferenceStore()); + + plugin = MavenPlugin.getDefault(); + } + + public void init(IWorkbench workbench) { + } + + /* + * Creates the field editors. Field editors are abstractions of the common GUI + * blocks needed to manipulate various types of preferences. Each field editor + * knows how to save and restore itself. + */ + public void createFieldEditors() { + parent = getFieldEditorParent(); + + addField(new StringFieldEditor(MavenPreferenceConstants.P_JIRA_USERNAME, Messages.getString("jira.username"), parent)); //$NON-NLS-1$ + + StringFieldEditor passwordEditor = new StringFieldEditor(MavenPreferenceConstants.P_JIRA_PASSWORD, Messages.getString("jira.password"), parent); //$NON-NLS-1$ + + addField(passwordEditor); + Text passwordField = passwordEditor.getTextControl(parent); + passwordField.setEchoChar('*'); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/RemoteArchetypeCatalogDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/RemoteArchetypeCatalogDialog.java new file mode 100644 index 00000000..649c89da --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/RemoteArchetypeCatalogDialog.java @@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.archetype.catalog.ArchetypeCatalog; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.RemoteCatalogFactory; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Remote Archetype catalog dialog + * + * @author Eugene Kuleshov + */ +public class RemoteArchetypeCatalogDialog extends TitleAreaDialog { + + /** + * + */ + private static final int VERIFY_ID = IDialogConstants.CLIENT_ID + 1; + + private static final String DIALOG_SETTINGS = RemoteArchetypeCatalogDialog.class.getName(); + + private static final String KEY_LOCATIONS = "catalogUrl"; //$NON-NLS-1$ + + private static final int MAX_HISTORY = 15; + + private String title; + + private String message; + + Combo catalogUrlCombo; + + private Text catalogDescriptionText; + + private IDialogSettings dialogSettings; + + private ArchetypeCatalogFactory archetypeCatalogFactory; + + Button verifyButton; + + + protected RemoteArchetypeCatalogDialog(Shell shell, ArchetypeCatalogFactory factory) { + super(shell); + this.archetypeCatalogFactory = factory; + this.title = Messages.RemoteArchetypeCatalogDialog_title; + this.message = Messages.RemoteArchetypeCatalogDialog_message; + setShellStyle(SWT.DIALOG_TRIM); + + IDialogSettings pluginSettings = MavenPlugin.getDefault().getDialogSettings(); + dialogSettings = pluginSettings.getSection(DIALOG_SETTINGS); + if(dialogSettings == null) { + dialogSettings = new DialogSettings(DIALOG_SETTINGS); + pluginSettings.addSection(dialogSettings); + } + } + + protected Control createContents(Composite parent) { + Control control = super.createContents(parent); + setTitle(title); + setMessage(message); + return control; + } + + protected Control createDialogArea(Composite parent) { + Composite composite1 = (Composite) super.createDialogArea(parent); + + Composite composite = new Composite(composite1, SWT.NONE); + composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginTop = 7; + gridLayout.marginWidth = 12; + gridLayout.numColumns = 2; + composite.setLayout(gridLayout); + + Label catalogLocationLabel = new Label(composite, SWT.NONE); + catalogLocationLabel.setText(Messages.RemoteArchetypeCatalogDialog_lblCatalog); + + catalogUrlCombo = new Combo(composite, SWT.NONE); + GridData gd_catalogLocationCombo = new GridData(SWT.FILL, SWT.CENTER, true, false); + gd_catalogLocationCombo.widthHint = 250; + catalogUrlCombo.setLayoutData(gd_catalogLocationCombo); + catalogUrlCombo.setItems(getSavedValues(KEY_LOCATIONS)); + + Label catalogDescriptionLabel = new Label(composite, SWT.NONE); + catalogDescriptionLabel.setText(Messages.RemoteArchetypeCatalogDialog_lblDesc); + + catalogDescriptionText = new Text(composite, SWT.BORDER); + catalogDescriptionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + if(archetypeCatalogFactory!=null) { + catalogUrlCombo.setText(archetypeCatalogFactory.getId()); + catalogDescriptionText.setText(archetypeCatalogFactory.getDescription()); + } + + ModifyListener modifyListener = new ModifyListener() { + public void modifyText(final ModifyEvent e) { + update(); + } + }; + catalogUrlCombo.addModifyListener(modifyListener); + catalogDescriptionText.addModifyListener(modifyListener); + + return composite; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite) + */ + protected Control createButtonBar(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + composite.setFont(parent.getFont()); + + // create help control if needed + if(isHelpAvailable()) { + createHelpControl(composite); + } + + verifyButton = createButton(composite, VERIFY_ID, Messages.RemoteArchetypeCatalogDialog_btnVerify, false); + verifyButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + verifyButton.setEnabled(false); + String url = catalogUrlCombo.getText(); + final RemoteCatalogFactory factory = new RemoteCatalogFactory(url, null, true); + + new Job(Messages.RemoteArchetypeCatalogDialog_job_download) { + protected IStatus run(IProgressMonitor monitor) { + IStatus status = Status.OK_STATUS; + ArchetypeCatalog catalog = null; + try { + catalog = factory.getArchetypeCatalog(); + } finally { + final IStatus s = status; + @SuppressWarnings("unchecked") + final List<Archetype> archetypes = catalog==null ? Collections.emptyList() : catalog.getArchetypes(); + getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + verifyButton.setEnabled(true); + if(!s.isOK()) { + setErrorMessage(NLS.bind(Messages.RemoteArchetypeCatalogDialog_error_read,s.getMessage())); + getButton(IDialogConstants.OK_ID).setEnabled(false); + } else if(archetypes.size()==0) { + setMessage(Messages.RemoteArchetypeCatalogDialog_error_empty, IStatus.WARNING); + } else { + setMessage(NLS.bind(Messages.RemoteArchetypeCatalogDialog_message_found, archetypes.size()), IStatus.INFO); + } + } + }); + } + return Status.OK_STATUS; + } + }.schedule(); + } + }); + + Label filler= new Label(composite, SWT.NONE); + filler.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL)); + layout.numColumns++; + + super.createButtonsForButtonBar(composite); // cancel button + + return composite; + } + + protected Button getButton(int id) { + return super.getButton(id); + } + + private String[] getSavedValues(String key) { + String[] array = dialogSettings.getArray(key); + return array == null ? new String[0] : array; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + public void create() { + super.create(); + getButton(IDialogConstants.OK_ID).setEnabled(false); + getButton(VERIFY_ID).setEnabled(false); + } + + protected void okPressed() { + String description = catalogDescriptionText.getText().trim(); + String location = catalogUrlCombo.getText().trim(); + + archetypeCatalogFactory = new RemoteCatalogFactory(location, description, true); + + saveValue(KEY_LOCATIONS, location); + + super.okPressed(); + } + + public ArchetypeCatalogFactory getArchetypeCatalogFactory() { + return archetypeCatalogFactory; + } + + private void saveValue(String key, String value) { + List<String> dirs = new ArrayList<String>(); + dirs.addAll(Arrays.asList(getSavedValues(key))); + + dirs.remove(value); + dirs.add(0, value); + + if(dirs.size() > MAX_HISTORY) { + dirs = dirs.subList(0, MAX_HISTORY); + } + + dialogSettings.put(key, dirs.toArray(new String[dirs.size()])); + } + + void update() { + boolean isValid = isValid(); + getButton(IDialogConstants.OK_ID).setEnabled(isValid); + getButton(VERIFY_ID).setEnabled(isValid); + } + + private boolean isValid() { + setErrorMessage(null); + setMessage(null, IStatus.WARNING); + + + String url = catalogUrlCombo.getText().trim(); + if(url.length()==0) { + setErrorMessage(Messages.RemoteArchetypeCatalogDialog_error_required); + verifyButton.setEnabled(false); + return false; + } + + verifyButton.setEnabled(true); + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/SimpleLifecycleMappingPropertyPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/SimpleLifecycleMappingPropertyPage.java new file mode 100644 index 00000000..76f8df82 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/preferences/SimpleLifecycleMappingPropertyPage.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.preferences; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.m2e.core.ui.internal.lifecycle.AbstractLifecyclePropertyPage; + + +/** + * Simple lifecycle mapping properties page that displays static text. + * + * @author igor + */ +public class SimpleLifecycleMappingPropertyPage extends AbstractLifecyclePropertyPage { + + private String message; + + public SimpleLifecycleMappingPropertyPage(String message) { + this.message = message; + } + + public Control createContents(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(GridData.FILL)); + Label noInfoLabel = new Label(composite, SWT.NONE); + noInfoLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 2, 1)); + noInfoLabel.setAlignment(SWT.CENTER); + noInfoLabel.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY)); + noInfoLabel.setData("name", "noInfoLabel"); //$NON-NLS-1$ //$NON-NLS-2$ + noInfoLabel.setText(getMessage()); + return composite; + } + + protected String getMessage() { + return message; + } + + public void performDefaults() { + } + + public boolean performOk() { + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchPage.java new file mode 100644 index 00000000..38cdb6f1 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchPage.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.search; + +import org.eclipse.jface.dialogs.DialogPage; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.search.ui.ISearchPage; +import org.eclipse.search.ui.ISearchPageContainer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; + +import org.eclipse.m2e.core.internal.Messages; + +/** + * Maven Search Page + * + * @author Eugene Kuleshov + */ +public class MavenSearchPage extends DialogPage implements ISearchPage { + + private Table table; + private Combo classNameText; + private Combo sha1Text; + private Combo versionText; + private Combo packagingIdText; + private Combo artifactIdText; + private Combo groupIdText; + + // private ISearchPageContainer container; + + public MavenSearchPage() { + } + + public MavenSearchPage(String title) { + super(title); + } + + public MavenSearchPage(String title, ImageDescriptor image) { + super(title, image); + } + + public void setContainer(ISearchPageContainer container) { + // this.container = container; + } + + public boolean performAction() { + // TODO Auto-generated method performAction + return false; + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(3, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + setControl(parent); + + Label groupIdLabel = new Label(composite, SWT.NONE); + groupIdLabel.setText(Messages.MavenSearchPage_lblGroupid); + + groupIdText = new Combo(composite, SWT.NONE); + groupIdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + Label artifactIdLabel = new Label(composite, SWT.NONE); + artifactIdLabel.setText(Messages.MavenSearchPage_lblArtifactid); + + artifactIdText = new Combo(composite, SWT.NONE); + artifactIdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + Label versionLabel = new Label(composite, SWT.NONE); + versionLabel.setText(Messages.MavenSearchPage_lblVersion); + + versionText = new Combo(composite, SWT.NONE); + versionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + Label packagingIdLabel = new Label(composite, SWT.NONE); + packagingIdLabel.setText(Messages.MavenSearchPage_lblPackaging); + + packagingIdText = new Combo(composite, SWT.NONE); + GridData packagingIdTextData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + packagingIdTextData.widthHint = 208; + packagingIdText.setLayoutData(packagingIdTextData); + + Label sha1Label = new Label(composite, SWT.NONE); + sha1Label.setText(Messages.MavenSearchPage_lblSha); + + sha1Text = new Combo(composite, SWT.NONE); + sha1Text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Button browseButton = new Button(composite, SWT.NONE); + browseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + browseButton.setText(Messages.MavenSearchPage_btnBrowse); + + Label classNameLabel = new Label(composite, SWT.NONE); + classNameLabel.setText(Messages.MavenSearchPage_lblClass); + + classNameText = new Combo(composite, SWT.NONE); + classNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + + Label separator = new Label(composite, SWT.HORIZONTAL | SWT.SEPARATOR); + GridData separatorData = new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1); + separatorData.heightHint = 15; + separatorData.minimumHeight = 15; + separator.setLayoutData(separatorData); + separator.setText(Messages.MavenSearchPage_separator); + + Label repositoriesLabel = new Label(composite, SWT.NONE); + repositoriesLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + repositoriesLabel.setText(Messages.MavenSearchPage_lblRepos); + + CheckboxTableViewer tableViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); + table = tableViewer.getTable(); + table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); + + Button selectAllButton = new Button(composite, SWT.NONE); + selectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + selectAllButton.setText(Messages.MavenSearchPage_btnSelect); + new Label(composite, SWT.NONE); + + Button deselectAllButton = new Button(composite, SWT.NONE); + deselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + deselectAllButton.setText(Messages.MavenSearchPage_btnUnselect); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResult.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResult.java new file mode 100644 index 00000000..a464aeea --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResult.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.search; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.search.ui.ISearchQuery; +import org.eclipse.search.ui.text.AbstractTextSearchResult; +import org.eclipse.search.ui.text.IEditorMatchAdapter; +import org.eclipse.search.ui.text.IFileMatchAdapter; + +/** + * Maven search result + * + * @author Eugene Kuleshov + */ +public class MavenSearchResult extends AbstractTextSearchResult { + + /* (non-Javadoc) + * @see org.eclipse.search.ui.ISearchResult#getQuery() + */ + public ISearchQuery getQuery() { + // TODO Auto-generated method getQuery + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.ISearchResult#getLabel() + */ + public String getLabel() { + // TODO Auto-generated method getLabel + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.ISearchResult#getImageDescriptor() + */ + public ImageDescriptor getImageDescriptor() { + // TODO Auto-generated method getImageDescriptor + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.ISearchResult#getTooltip() + */ + public String getTooltip() { + // TODO Auto-generated method getTooltip + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchResult#getEditorMatchAdapter() + */ + public IEditorMatchAdapter getEditorMatchAdapter() { + // TODO Auto-generated method getEditorMatchAdapter + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchResult#getFileMatchAdapter() + */ + public IFileMatchAdapter getFileMatchAdapter() { + // TODO Auto-generated method getFileMatchAdapter + return null; + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResultPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResultPage.java new file mode 100644 index 00000000..d0c28aae --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/search/MavenSearchResultPage.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.search; + +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.search.ui.text.AbstractTextSearchViewPage; +import org.eclipse.ui.IMemento; + +/** + * Maven search result page + * + * @author Eugene Kuleshov + */ +public class MavenSearchResultPage extends AbstractTextSearchViewPage { + + public MavenSearchResultPage() { + super(FLAG_LAYOUT_TREE | FLAG_LAYOUT_FLAT); + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#configureTableViewer(org.eclipse.jface.viewers.TableViewer) + */ + protected void configureTableViewer(TableViewer viewer) { + // TODO Auto-generated method configureTableViewer + + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#configureTreeViewer(org.eclipse.jface.viewers.TreeViewer) + */ + protected void configureTreeViewer(TreeViewer viewer) { + // TODO Auto-generated method configureTreeViewer + + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#clear() + */ + protected void clear() { + // TODO Auto-generated method clear + + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#elementsChanged(java.lang.Object[]) + */ + protected void elementsChanged(Object[] objects) { + // TODO Auto-generated method elementsChanged + + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#restoreState(org.eclipse.ui.IMemento) + */ + public void restoreState(IMemento memento) { + super.restoreState(memento); + + // TODO Auto-generated method restoreState + + } + + /* (non-Javadoc) + * @see org.eclipse.search.ui.text.AbstractTextSearchViewPage#saveState(org.eclipse.ui.IMemento) + */ + public void saveState(IMemento memento) { + super.saveState(memento); + // TODO Auto-generated method saveState + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/MavenRepositoryView.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/MavenRepositoryView.java new file mode 100644 index 00000000..08547ca7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/MavenRepositoryView.java @@ -0,0 +1,596 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.actions.BaseSelectionListenerAction; +import org.eclipse.ui.part.DrillDownAdapter; +import org.eclipse.ui.part.ViewPart; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.MaterializeAction; +import org.eclipse.m2e.core.actions.OpenPomAction; +import org.eclipse.m2e.core.index.IndexListener; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.IndexedArtifactGroup; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.ui.internal.views.nodes.AbstractIndexedRepositoryNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.IArtifactNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.IndexedArtifactFileNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.LocalRepositoryNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.RepositoryNode; +import org.eclipse.m2e.core.util.M2EUtils; + + +/** + * Maven repository view + * + * @author dyocum + */ +public class MavenRepositoryView extends ViewPart { + private static final String ENABLE_FULL = Messages.MavenRepositoryView_enable_full; + private static final String ENABLED_FULL = Messages.MavenRepositoryView_enabled_full; + private static final String DISABLE_DETAILS = Messages.MavenRepositoryView_disable_details; + private static final String DISABLED_DETAILS = Messages.MavenRepositoryView_details_disabled; + private static final String ENABLE_MIN = Messages.MavenRepositoryView_enable_minimum; + private static final String ENABLED_MIN = Messages.MavenRepositoryView_minimum_enabled; + + private IndexManager indexManager = MavenPlugin.getDefault().getIndexManager(); + + private IAction collapseAllAction; + + private IAction reloadSettings; + + BaseSelectionListenerAction openPomAction; + + private BaseSelectionListenerAction updateAction; + + private BaseSelectionListenerAction rebuildAction; + + private DisableIndexAction disableAction; + private EnableMinIndexAction enableMinAction; + private EnableFullIndexAction enableFullAction; + + private BaseSelectionListenerAction copyUrlAction; + + private BaseSelectionListenerAction materializeProjectAction; + + TreeViewer viewer; + private RepositoryViewContentProvider contentProvider; + + private DrillDownAdapter drillDownAdapter; + + private IndexListener indexListener; + + public void setFocus() { + viewer.getControl().setFocus(); + } + + public void createPartControl(Composite parent) { + viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + contentProvider = new RepositoryViewContentProvider(); + viewer.setContentProvider(contentProvider); + viewer.setLabelProvider(new RepositoryViewLabelProvider(viewer.getTree().getFont())); + + viewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + + } + }); + viewer.setInput(getViewSite()); + drillDownAdapter = new DrillDownAdapter(viewer); + + makeActions(); + hookContextMenu(); + + viewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + openPomAction.run(); + } + }); + + contributeToActionBars(); + this.indexListener = new IndexListener() { + + public void indexAdded(IRepository repository) { + refreshView(); + } + + public void indexChanged(IRepository repository) { + refreshView(); + } + + public void indexRemoved(IRepository repository) { + refreshView(); + } + + public void indexUpdating(IRepository repository){ + Display.getDefault().asyncExec(new Runnable(){ + public void run(){ + viewer.refresh(true); + } + }); + } + }; + + indexManager.addIndexListener(this.indexListener); + } + + private void hookContextMenu() { + MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + MavenRepositoryView.this.fillContextMenu(manager); + } + }); + + Menu menu = menuMgr.createContextMenu(viewer.getControl()); + viewer.getControl().setMenu(menu); + getSite().registerContextMenu(menuMgr, viewer); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalPullDown(bars.getMenuManager()); + fillLocalToolBar(bars.getToolBarManager()); + } + + private void fillLocalPullDown(IMenuManager manager) { + manager.add(new Separator()); + manager.add(collapseAllAction); + manager.add(reloadSettings); + } + + protected List<AbstractIndexedRepositoryNode> getSelectedRepositoryNodes(List elements){ + ArrayList<AbstractIndexedRepositoryNode> list = new ArrayList<AbstractIndexedRepositoryNode>(); + if (elements != null) { + for(int i=0;i<elements.size();i++){ + Object elem = elements.get(i); + if(elem instanceof AbstractIndexedRepositoryNode) { + list.add((AbstractIndexedRepositoryNode)elem); + } + } + } + return list; + } + protected List<IArtifactNode> getArtifactNodes(List elements){ + if(elements == null || elements.size() == 0){ + return null; + } + ArrayList<IArtifactNode> list = new ArrayList<IArtifactNode>(); + for(int i=0;i<elements.size();i++){ + Object elem = elements.get(i); + if(elem instanceof IArtifactNode){ + IArtifactNode node = (IArtifactNode)elem; + list.add(node); + } + } + return list; + } + void fillContextMenu(IMenuManager manager) { + manager.add(openPomAction); + manager.add(copyUrlAction); + manager.add(materializeProjectAction); + manager.add(new Separator()); + manager.add(updateAction); + manager.add(rebuildAction); + manager.add(new Separator()); + manager.add(disableAction); + manager.add(enableMinAction); + manager.add(enableFullAction); +// manager.add(deleteFromLocalAction); + manager.add(new Separator()); + manager.add(collapseAllAction); + manager.add(new Separator()); + drillDownAdapter.addNavigationActions(manager); + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + private void fillLocalToolBar(IToolBarManager manager) { + manager.add(new Separator()); + manager.add(collapseAllAction); + manager.add(reloadSettings); + manager.add(new Separator()); + drillDownAdapter.addNavigationActions(manager); + } + + private void makeActions() { + collapseAllAction = new Action(Messages.MavenRepositoryView_btnCollapse) { + public void run() { + viewer.collapseAll(); + } + }; + collapseAllAction.setToolTipText(Messages.MavenRepositoryView_btnCollapse_tooltip); + collapseAllAction.setImageDescriptor(MavenImages.COLLAPSE_ALL); + reloadSettings = new Action(Messages.MavenRepositoryView_action_reload){ + public void run(){ + String msg = Messages.MavenRepositoryView_reload_msg; + boolean res = MessageDialog.openConfirm(getViewSite().getShell(), // + Messages.MavenRepositoryView_reload_title, msg); + if(res){ + Job job = new WorkspaceJob(Messages.MavenRepositoryView_job_reloading) { + public IStatus runInWorkspace(IProgressMonitor monitor) { + try { + MavenPlugin.getDefault().getMaven().reloadSettings(); + } catch(CoreException ex) { + return ex.getStatus(); + } + return Status.OK_STATUS; + } + }; + job.schedule(); + } + } + }; + + reloadSettings.setImageDescriptor(MavenImages.REFRESH); +// deleteFromLocalAction = new BaseSelectionListenerAction("Delete from Repository") { +// public void run() { +// List<IArtifactNode> nodes = getArtifactNodes(getStructuredSelection().toList()); +// if(nodes != null){ +// for(IArtifactNode node : nodes){ +// String key = node.getDocumentKey(); +// System.out.println("key: "+key); +// ((NexusIndexManager)MavenPlugin.getDefault().getIndexManager()).removeDocument("local", null, key); +// } +// } +// } +// +// protected boolean updateSelection(IStructuredSelection selection) { +// List<IArtifactNode> nodes = getArtifactNodes(getStructuredSelection().toList()); +// return (nodes != null && nodes.size() > 0); +// } +// }; +// deleteFromLocalAction.setToolTipText("Delete the selected GAV from the local repository"); + //updateAction.setImageDescriptor(MavenImages.UPD_INDEX); + + + updateAction = new BaseSelectionListenerAction(Messages.MavenRepositoryView_action_update) { + public void run() { + List<AbstractIndexedRepositoryNode> nodes = getSelectedRepositoryNodes(getStructuredSelection().toList()); + for(AbstractIndexedRepositoryNode node : nodes) { + if (node instanceof RepositoryNode) { + ((RepositoryNode) node).getIndex().scheduleIndexUpdate(false); + } + } + } + + protected boolean updateSelection(IStructuredSelection selection) { + int indexCount = 0; + for (AbstractIndexedRepositoryNode node : getSelectedRepositoryNodes(selection.toList())) { + if (node instanceof RepositoryNode && node.isEnabledIndex()) { + indexCount ++; + } + } + if(indexCount > 1){ + setText(Messages.MavenRepositoryView_update_more); + } else { + setText(Messages.MavenRepositoryView_update_one); + } + return indexCount > 0; + } + }; + updateAction.setToolTipText(Messages.MavenRepositoryView_btnUpdate_tooltip); + updateAction.setImageDescriptor(MavenImages.UPD_INDEX); + + rebuildAction = new BaseSelectionListenerAction(Messages.MavenRepositoryView_action_rebuild) { + public void run() { + List<AbstractIndexedRepositoryNode> nodes = getSelectedRepositoryNodes(getStructuredSelection().toList()); + if(nodes.size() > 0){ + if(nodes.size() == 1){ + NexusIndex index = nodes.get(0).getIndex(); + if (index != null) { + String repositoryUrl = index.getRepositoryUrl(); + String msg = NLS.bind(Messages.MavenRepositoryView_rebuild_msg, repositoryUrl); + boolean res = MessageDialog.openConfirm(getViewSite().getShell(), // + Messages.MavenRepositoryView_rebuild_title, msg); + if(res) { + index.scheduleIndexUpdate(true); + } + } + } else { + String msg = Messages.MavenRepositoryView_rebuild_msg2; + boolean res = MessageDialog.openConfirm(getViewSite().getShell(), // + Messages.MavenRepositoryView_rebuild_title2, msg); + if(res) { + for(AbstractIndexedRepositoryNode node : nodes){ + NexusIndex index = node.getIndex(); + if (index != null) { + index.scheduleIndexUpdate(true); + } + } + } + } + } + } + + protected boolean updateSelection(IStructuredSelection selection) { + int indexCount = 0; + for (AbstractIndexedRepositoryNode node : getSelectedRepositoryNodes(selection.toList())) { + if ((node instanceof LocalRepositoryNode) || node.isEnabledIndex()) { + indexCount ++; + } + } + if(indexCount > 1){ + setText(Messages.MavenRepositoryView_rebuild_many); + } else { + setText(Messages.MavenRepositoryView_rebuild_one); + } + return indexCount > 0; + } + }; + + rebuildAction.setToolTipText(Messages.MavenRepositoryView_action_rebuild_tooltip); + rebuildAction.setImageDescriptor(MavenImages.REBUILD_INDEX); + + disableAction = new DisableIndexAction(); + + disableAction.setToolTipText(Messages.MavenRepositoryView_action_disable_tooltip); + disableAction.setImageDescriptor(MavenImages.REBUILD_INDEX); + + enableMinAction = new EnableMinIndexAction(); + enableMinAction.setToolTipText(Messages.MavenRepositoryView_action_enable_tooltip); + enableMinAction.setImageDescriptor(MavenImages.REBUILD_INDEX); + + enableFullAction = new EnableFullIndexAction(); + enableFullAction.setToolTipText(Messages.MavenRepositoryView_action_enableFull_tooltip); + enableFullAction.setImageDescriptor(MavenImages.REBUILD_INDEX); + + openPomAction = new BaseSelectionListenerAction(Messages.MavenRepositoryView_action_open) { + public void run() { + ISelection selection = viewer.getSelection(); + Object element = ((IStructuredSelection) selection).getFirstElement(); + if(element instanceof IndexedArtifactFileNode) { + IndexedArtifactFile f = ((IndexedArtifactFileNode) element).getIndexedArtifactFile(); + OpenPomAction.openEditor(f.group, f.artifact, f.version, null); + } + } + + protected boolean updateSelection(IStructuredSelection selection) { + return selection.getFirstElement() instanceof IndexedArtifactFile; + } + }; + openPomAction.setToolTipText(Messages.MavenRepositoryView_action_open_tooltip); + openPomAction.setImageDescriptor(MavenImages.POM); + + copyUrlAction = new BaseSelectionListenerAction(Messages.MavenRepositoryView_action_copy) { + public void run() { + Object element = getStructuredSelection().getFirstElement(); + String url = null; + if(element instanceof RepositoryNode) { + url = ((RepositoryNode) element).getRepositoryUrl(); + } else if(element instanceof IndexedArtifactGroup) { + IndexedArtifactGroup group = (IndexedArtifactGroup) element; + String repositoryUrl = group.getRepository().getUrl(); + if(!repositoryUrl.endsWith("/")) { //$NON-NLS-1$ + repositoryUrl += "/"; //$NON-NLS-1$ + } + url = repositoryUrl + group.getPrefix().replace('.', '/'); + } else if(element instanceof IndexedArtifact) { + // + } else if(element instanceof IndexedArtifactFile) { + // + } + if(url != null) { + Clipboard clipboard = new Clipboard(Display.getCurrent()); + clipboard.setContents(new String[] {url}, new Transfer[] {TextTransfer.getInstance()}); + clipboard.dispose(); + } + } + + protected boolean updateSelection(IStructuredSelection selection) { + Object element = selection.getFirstElement(); + return element instanceof RepositoryNode; + } + }; + copyUrlAction.setToolTipText(Messages.MavenRepositoryView_action_copy_tooltip); + copyUrlAction.setImageDescriptor(MavenImages.COPY); + + materializeProjectAction = new BaseSelectionListenerAction(Messages.MavenRepositoryView_action_materialize) { + public void run() { + Object element = getStructuredSelection().getFirstElement(); + if(element instanceof IndexedArtifactFileNode){ + MaterializeAction action = new MaterializeAction(); + StructuredSelection sel = new StructuredSelection(new Object[]{((IndexedArtifactFileNode) element).getIndexedArtifactFile()}); + action.selectionChanged(this, sel); + action.run(this); + } + } + + protected boolean updateSelection(IStructuredSelection selection) { + return selection.getFirstElement() instanceof IndexedArtifactFileNode; + } + }; + materializeProjectAction.setImageDescriptor(MavenImages.IMPORT_PROJECT); + + viewer.addSelectionChangedListener(openPomAction); + viewer.addSelectionChangedListener(updateAction); + viewer.addSelectionChangedListener(disableAction); + viewer.addSelectionChangedListener(enableMinAction); + viewer.addSelectionChangedListener(enableFullAction); + viewer.addSelectionChangedListener(rebuildAction); + viewer.addSelectionChangedListener(copyUrlAction); + viewer.addSelectionChangedListener(materializeProjectAction); + } + + protected void setIndexDetails(AbstractIndexedRepositoryNode node, String details) { + if (node != null && node.getIndex() != null) { + try { + node.getIndex().setIndexDetails(details); + } catch(CoreException ex) { + M2EUtils.showErrorDialog(this.getViewSite().getShell(), Messages.MavenRepositoryView_error_title, Messages.MavenRepositoryView_error_message, ex); + } + } + } + + protected AbstractIndexedRepositoryNode getSelectedRepositoryNode(IStructuredSelection selection) { + List elements = selection.toList(); + if (elements.size() != 1) { + return null; + } + Object element = elements.get(0); + return element instanceof AbstractIndexedRepositoryNode? (AbstractIndexedRepositoryNode) element: null; + } + + public void dispose() { + viewer.removeSelectionChangedListener(materializeProjectAction); + viewer.removeSelectionChangedListener(copyUrlAction); + viewer.removeSelectionChangedListener(rebuildAction); + viewer.removeSelectionChangedListener(disableAction); + viewer.removeSelectionChangedListener(enableMinAction); + viewer.removeSelectionChangedListener(enableFullAction); + viewer.removeSelectionChangedListener(updateAction); + viewer.removeSelectionChangedListener(openPomAction); + indexManager.removeIndexListener(this.indexListener); + super.dispose(); + } + + void refreshView() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + Object[] expandedElems = viewer.getExpandedElements(); + if (!viewer.getControl().isDisposed()) { + viewer.setInput(getViewSite()); + if(expandedElems != null && expandedElems.length > 0){ + viewer.setExpandedElements(expandedElems); + } + } + } + }); + }; + + /** + * Base Selection Listener does not allow the style (radio button/check) to be set. + * This base class listens to selections and sets the appropriate index value + * depending on its value + * AbstractIndexAction + * + * @author dyocum + */ + abstract class AbstractIndexAction extends Action implements ISelectionChangedListener{ + + protected abstract String getDetailsValue(); + protected abstract String getActionText(); + + public AbstractIndexAction(String text, int style){ + super(text, style); + } + + public void run() { + IStructuredSelection sel = (IStructuredSelection)viewer.getSelection(); + setIndexDetails(getSelectedRepositoryNode(sel), getDetailsValue()); + } + + /* + */ + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection sel = (IStructuredSelection)event.getSelection(); + updateSelection(sel); + } + + protected void updateSelection(IStructuredSelection selection) { + AbstractIndexedRepositoryNode node = getSelectedRepositoryNode(selection); + updateIndexDetails(node); + setText(getActionText()); + boolean enabled = (node != null && node instanceof RepositoryNode); + this.setEnabled(enabled); + } + + protected void updateIndexDetails(AbstractIndexedRepositoryNode node){ + if(node == null || node.getIndex() == null){ + return; + } + NexusIndex index = node.getIndex(); + setChecked(getDetailsValue().equals(index.getIndexDetails())); + } + + } + + class DisableIndexAction extends AbstractIndexAction { + public DisableIndexAction(){ + super(DISABLE_DETAILS, IAction.AS_CHECK_BOX); + } + + protected String getDetailsValue(){ + return NexusIndex.DETAILS_DISABLED; + } + protected String getActionText(){ + return isChecked() ? DISABLED_DETAILS : DISABLE_DETAILS; + } + } + + class EnableMinIndexAction extends AbstractIndexAction { + public EnableMinIndexAction(){ + super(ENABLE_MIN, IAction.AS_CHECK_BOX); + } + + protected String getDetailsValue(){ + return NexusIndex.DETAILS_MIN; + } + protected String getActionText(){ + return isChecked() ? ENABLED_MIN : ENABLE_MIN; + } + } + + class EnableFullIndexAction extends AbstractIndexAction { + public EnableFullIndexAction(){ + super(ENABLE_FULL, IAction.AS_CHECK_BOX); + } + + protected String getDetailsValue(){ + return NexusIndex.DETAILS_FULL; + } + protected String getActionText(){ + return isChecked() ? ENABLED_FULL : ENABLE_FULL; + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewContentProvider.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewContentProvider.java new file mode 100644 index 00000000..9a0b3fe0 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewContentProvider.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.IViewSite; + +import org.eclipse.m2e.core.ui.internal.views.nodes.CustomRepositoriesNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.GlobalRepositoriesNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.IMavenRepositoryNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.LocalRepositoryRootNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.ProjectRepositoriesNode; + +/** + * RepositoryViewContentProvider + * + * @author dyocum + */ +public class RepositoryViewContentProvider implements IStructuredContentProvider, ITreeContentProvider { + + private LocalRepositoryRootNode localNode; + private GlobalRepositoriesNode globalNode; + private ProjectRepositoriesNode projectNode; + private CustomRepositoriesNode customNode; + + public RepositoryViewContentProvider() { + } + + public void inputChanged(Viewer v, Object oldInput, Object newInput) { + } + + public void dispose() { + } + + public Object[] getElements(Object parent) { + return getChildren(parent); + } + + public Object getParent(Object child) { + return null; + } + + public boolean hasChildren(Object parent) { + if(parent instanceof IMavenRepositoryNode){ + return ((IMavenRepositoryNode)parent).hasChildren(); + } + return false; + } + + public Object[] getRootNodes(){ + if(localNode == null){ + localNode = new LocalRepositoryRootNode(); + + } + if(globalNode == null){ + globalNode = new GlobalRepositoriesNode(); + } + if(projectNode == null) { + projectNode = new ProjectRepositoriesNode(); + } + if(customNode == null) { + customNode = new CustomRepositoriesNode(); + } + return new Object[]{localNode, globalNode, projectNode, customNode}; + } + + public Object[] getChildren(Object parent) { + if(parent instanceof IViewSite){ + return getRootNodes(); + } else if(parent instanceof IMavenRepositoryNode){ + return ((IMavenRepositoryNode)parent).getChildren(); + } + return new Object[0]; + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewLabelProvider.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewLabelProvider.java new file mode 100644 index 00000000..0ac86c34 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/RepositoryViewLabelProvider.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views; + +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.m2e.core.ui.internal.views.nodes.IMavenRepositoryNode; +import org.eclipse.m2e.core.ui.internal.views.nodes.RepositoryNode; +import org.eclipse.m2e.core.util.M2EUtils; + +/** + * RepositoryViewLabelProvider + * + * @author dyocum + */ +public class RepositoryViewLabelProvider extends LabelProvider implements IColorProvider, IFontProvider { + + private Font italicFont; + public RepositoryViewLabelProvider(Font treeFont){ + int size = 0; + FontData[] data = treeFont.getFontData(); + if(data == null){ + size = 12; + } else { + for(int i=0;i<data.length;i++){ + size = Math.max(size, data[i].getHeight()); + } + } + italicFont = M2EUtils.deriveFont(treeFont, SWT.ITALIC, size); + } + + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() + */ + public void dispose() { + italicFont.dispose(); + super.dispose(); + } + + + public String getText(Object obj) { + if(obj instanceof IMavenRepositoryNode){ + return ((IMavenRepositoryNode)obj).getName(); + } + return obj.toString(); + } + + public Image getImage(Object obj) { + if(obj instanceof IMavenRepositoryNode){ + return ((IMavenRepositoryNode)obj).getImage(); + } + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT); + } + + public Color getBackground(Object element) { + return null; + } + + public Color getForeground(Object element) { + if(element instanceof RepositoryNode){ + if(((RepositoryNode)element).isEnabledIndex()){ + return Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + } + return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); + } + return Display.getDefault().getSystemColor(SWT.COLOR_BLACK); + } + + public Font getFont(Object element) { + if(element instanceof IMavenRepositoryNode){ + boolean updating = ((IMavenRepositoryNode)element).isUpdating(); + return updating ? italicFont : null; + } + return null; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractIndexedRepositoryNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractIndexedRepositoryNode.java new file mode 100644 index 00000000..a6fb9f55 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractIndexedRepositoryNode.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.Arrays; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.index.IndexedArtifactGroup; +import org.eclipse.m2e.core.internal.index.NexusIndex; + + +/** + * AbstractIndexedRepository + * + * @author igor + */ +public abstract class AbstractIndexedRepositoryNode implements IMavenRepositoryNode { + + protected static final Object[] NO_CHILDREN = new Object[0]; + + protected final NexusIndex index; + + protected AbstractIndexedRepositoryNode(NexusIndex index) { + this.index = index; + } + + public Object[] getChildren() { + + if(index == null) { + return NO_CHILDREN; + } + + try { + IndexedArtifactGroup[] rootGroups = index.getRootIndexedArtifactGroups(); + if(rootGroups == null) { + return NO_CHILDREN; + } + IndexedArtifactGroupNode[] children = new IndexedArtifactGroupNode[rootGroups.length]; + Arrays.sort(rootGroups); + for(int i = 0; i < rootGroups.length; i++ ) { + children[i] = new IndexedArtifactGroupNode(rootGroups[i]); + } + return children; + } catch(CoreException ex) { + MavenLogger.log(ex); + return NO_CHILDREN; + } + } + + public Image getImage() { + return MavenImages.IMG_INDEX; + } + + public boolean hasChildren() { + return index != null; + } + + public boolean isUpdating() { + return index != null && index.isUpdating(); + } + + public NexusIndex getIndex() { + return index; + } + + public String getRepositoryUrl() { + return index.getRepositoryUrl(); + } + + public boolean isEnabledIndex() { + return index != null && index.isEnabled(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractRepositoriesNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractRepositoriesNode.java new file mode 100644 index 00000000..bb80d447 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/AbstractRepositoriesNode.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + +/** + * AbstractRepositoriesNode + * + * @author igor + */ +public abstract class AbstractRepositoriesNode implements IMavenRepositoryNode { + + protected final NexusIndexManager indexManager = (NexusIndexManager) MavenPlugin.getDefault().getIndexManager(); + protected final IRepositoryRegistry repositoryRegistry = MavenPlugin.getDefault().getRepositoryRegistry(); + + public Object[] getChildren() { + + ArrayList<Object> mirrorNodes = new ArrayList<Object>(); + ArrayList<Object> globalRepoNodes = new ArrayList<Object>(); + + for (IRepository repo : getRepositories()) { + NexusIndex index = indexManager.getIndex(repo); + RepositoryNode node = new RepositoryNode(index); + if (repo.getMirrorOf() != null) { + mirrorNodes.add(node); + } else { + globalRepoNodes.add(node); + } + } + + ArrayList<Object> nodes = new ArrayList<Object>(); + nodes.addAll(mirrorNodes); + nodes.addAll(globalRepoNodes); + + return nodes.toArray(new Object[nodes.size()]); + } + + protected abstract List<IRepository> getRepositories(); + + public String toString() { + return getName(); + } + + public boolean hasChildren() { + Object[] kids = getChildren(); + return kids != null && kids.length > 0; + } + + public Image getImage() { + return MavenImages.IMG_INDEXES; + } + + public boolean isUpdating() { + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/CustomRepositoriesNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/CustomRepositoriesNode.java new file mode 100644 index 00000000..678dcbdd --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/CustomRepositoriesNode.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.List; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + +/** + * CustomRepositoriesNode + * + * @author igor + */ +public class CustomRepositoriesNode extends AbstractRepositoriesNode { + + protected List<IRepository> getRepositories() { + return repositoryRegistry.getRepositories(IRepositoryRegistry.SCOPE_UNKNOWN); + } + + public String getName() { + return Messages.CustomRepositoriesNode_name; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/GlobalRepositoriesNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/GlobalRepositoriesNode.java new file mode 100644 index 00000000..7eef0deb --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/GlobalRepositoriesNode.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.List; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + + +/** + * Parent node for all artifact repositories and mirrors defined in settings.xml. + * + * @author dyocum + */ +public class GlobalRepositoriesNode extends AbstractRepositoriesNode { + + public String getName() { + return Messages.GlobalRepositoriesNode_name; + } + + protected List<IRepository> getRepositories() { + return repositoryRegistry.getRepositories(IRepositoryRegistry.SCOPE_SETTINGS); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IArtifactNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IArtifactNode.java new file mode 100644 index 00000000..54c908fb --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IArtifactNode.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + + +/** + * AbstractArtifactNode + * + * @author dyocum + */ +public interface IArtifactNode { + public String getDocumentKey(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IMavenRepositoryNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IMavenRepositoryNode.java new file mode 100644 index 00000000..b296ca7f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IMavenRepositoryNode.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.swt.graphics.Image; + +/** + * MavenRepositoryRootNode + * + * @author dyocum + */ +public interface IMavenRepositoryNode { + + public Object[] getChildren(); + public String getName(); + public Image getImage(); + public boolean hasChildren(); + public boolean isUpdating(); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactFileNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactFileNode.java new file mode 100644 index 00000000..4f9d5702 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactFileNode.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; + +/** + * IndexedArtifactFileNode + * + * @author dyocum + */ +public class IndexedArtifactFileNode implements IMavenRepositoryNode, IArtifactNode { + + private IndexedArtifactFile artifactFile; + + public IndexedArtifactFileNode(IndexedArtifactFile artifactFile){ + this.artifactFile = artifactFile; + } + + public IndexedArtifactFile getIndexedArtifactFile(){ + return this.artifactFile; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getChildren() + */ + public Object[] getChildren() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getName() + */ + public String getName() { + String label = artifactFile.artifact; + if(artifactFile.classifier != null) { + label += " : " + artifactFile.classifier; //$NON-NLS-1$ + } + if(artifactFile.version != null) { + label += " : " + artifactFile.version; //$NON-NLS-1$ + } + return label; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#hasChildren() + */ + public boolean hasChildren() { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#getImage() + */ + public Image getImage() { + if(artifactFile.sourcesExists == IIndex.PRESENT) { + return MavenImages.IMG_VERSION_SRC; + } + return MavenImages.IMG_VERSION; + + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IArtifactNode#getDocumentKey() + */ + public String getDocumentKey() { + return NexusIndexManager.getDocumentKey(artifactFile.getArtifactKey()); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#isUpdating() + */ + public boolean isUpdating() { + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactGroupNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactGroupNode.java new file mode 100644 index 00000000..28f5b5c4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactGroupNode.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.internal.index.IndexedArtifactGroup; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; + +/** + * IndexedArtifactGroupNode + * + * @author dyocum + */ +public class IndexedArtifactGroupNode implements IMavenRepositoryNode, IArtifactNode { + + private IndexedArtifactGroup indexedArtifactGroup; + private Object[] kids = null; + public IndexedArtifactGroupNode(IndexedArtifactGroup group){ + this.indexedArtifactGroup = group; + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getChildren() + */ + public Object[] getChildren() { + NexusIndexManager indexManager = (NexusIndexManager) MavenPlugin.getDefault().getIndexManager(); + + IndexedArtifactGroup resolvedGroup = indexManager.resolveGroup(indexedArtifactGroup); + //IndexedArtifactGroup resolvedGroup = indexedArtifactGroup; + ArrayList<Object> results = new ArrayList<Object>(); + Collection<IndexedArtifactGroup> groups = resolvedGroup.getNodes().values(); + for(IndexedArtifactGroup group : groups){ + IndexedArtifactGroupNode node = new IndexedArtifactGroupNode(group); + results.add(node); + } + + Collection<IndexedArtifact> artifacts = resolvedGroup.getFiles().values(); // IndexedArtifact + for(IndexedArtifact artifact : artifacts){ + IndexedArtifactNode artifactNode = new IndexedArtifactNode(artifact); + results.add(artifactNode); + } + kids = results.toArray(new Object[results.size()]); + return kids; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getName() + */ + public String getName() { + String prefix = indexedArtifactGroup.getPrefix(); + int n = prefix.lastIndexOf('.'); + return n < 0 ? prefix : prefix.substring(n + 1); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#hasChildren() + */ + public boolean hasChildren() { +// if(kids == null){ +// kids = getChildren(); +// } +// return kids != null && kids.length > 0; + return true; + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#getImage() + */ + public Image getImage() { + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IArtifactNode#getDocumentKey() + */ + public String getDocumentKey() { + return indexedArtifactGroup.getPrefix(); + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#isUpdating() + */ + public boolean isUpdating() { + // TODO Auto-generated method isUpdating + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactNode.java new file mode 100644 index 00000000..23c3a8e0 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/IndexedArtifactNode.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.ArrayList; +import java.util.Set; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; + +/** + * IndexedArtifactNode + * + * @author dyocum + */ +public class IndexedArtifactNode implements IMavenRepositoryNode, IArtifactNode { + + private IndexedArtifact artifact; + private Object[] kids = null; + public IndexedArtifactNode(IndexedArtifact artifact){ + this.artifact = artifact; + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getChildren() + */ + public Object[] getChildren() { + Set<IndexedArtifactFile> files = artifact.getFiles(); + if(files == null){ + return new Object[0]; + } + ArrayList<Object> fileList = new ArrayList<Object>(); + for(IndexedArtifactFile iaf : files){ + fileList.add(new IndexedArtifactFileNode(iaf)); + } + kids = fileList.toArray(new IndexedArtifactFileNode[fileList.size()]); + return kids; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#getName() + */ + public String getName() { + // return a.group + ":" + a.artifact; + String pkg = artifact.getPackaging(); + if(pkg == null){ + pkg = Messages.IndexedArtifactNode_no_pack; + } + return artifact.getArtifactId() + " - " + pkg; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.IMavenRepositoryNode#hasChildren() + */ + public boolean hasChildren() { + //return kids != null && kids.length > 0; + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#getImage() + */ + public Image getImage() { + return MavenImages.IMG_JAR; + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IArtifactNode#getDocumentKey() + */ + public String getDocumentKey() { + return artifact.getArtifactId(); + } + /* (non-Javadoc) + * @see org.eclipse.m2e.ui.internal.views.nodes.IMavenRepositoryNode#isUpdating() + */ + public boolean isUpdating() { + // TODO Auto-generated method isUpdating + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryNode.java new file mode 100644 index 00000000..e46218be --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryNode.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.repository.IRepository; + +/** + * LocalRepositoryNode + * + * @author igor + */ +public class LocalRepositoryNode extends AbstractIndexedRepositoryNode { + + public LocalRepositoryNode(NexusIndex index) { + super(index); + } + + public String getName() { + IRepository repository = index.getRepository(); + StringBuilder sb = new StringBuilder(); + sb.append(Messages.LocalRepositoryNode_local); + if (repository.getBasedir() != null) { + sb.append(" (").append(repository.getBasedir().getAbsolutePath()).append(')'); //$NON-NLS-1$ + } + return sb.toString(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryRootNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryRootNode.java new file mode 100644 index 00000000..69b6bb17 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/LocalRepositoryRootNode.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; + +/** + * LocalRepositoryNode + * + * @author dyocum + */ +public class LocalRepositoryRootNode implements IMavenRepositoryNode{ + + public Object[] getChildren() { + NexusIndexManager indexManager = (NexusIndexManager) MavenPlugin.getDefault().getIndexManager(); + NexusIndex localIndex = indexManager.getLocalIndex(); + NexusIndex workspaceIndex = indexManager.getWorkspaceIndex(); + return new Object[]{ + new LocalRepositoryNode(localIndex), + new WorkspaceRepositoryNode(workspaceIndex) + }; + } + + public String getName() { + return Messages.LocalRepositoryRootNode_name; + } + + public boolean hasChildren() { + return true; + } + + public Image getImage() { + return MavenImages.IMG_INDEXES; + } + + public boolean isUpdating() { + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/ProjectRepositoriesNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/ProjectRepositoriesNode.java new file mode 100644 index 00000000..2c96cc27 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/ProjectRepositoriesNode.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import java.util.ArrayList; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.internal.index.NexusIndexManager; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.repository.IRepositoryRegistry; + + +/** + * Parent node for all artifact repositories configured in pom.xml files. + */ +public class ProjectRepositoriesNode implements IMavenRepositoryNode { + + private NexusIndexManager indexManager = (NexusIndexManager) MavenPlugin.getDefault().getIndexManager(); + private IRepositoryRegistry repositoryRegistry = MavenPlugin.getDefault().getRepositoryRegistry(); + + public Object[] getChildren() { + ArrayList<Object> nodes = new ArrayList<Object>(); + for(IRepository repo : repositoryRegistry.getRepositories(IRepositoryRegistry.SCOPE_PROJECT)) { + NexusIndex index = indexManager.getIndex(repo); + RepositoryNode node = new RepositoryNode(index); + nodes.add(node); + } + return nodes.toArray(new Object[nodes.size()]); + } + + public Image getImage() { + return MavenImages.IMG_INDEXES; + } + + public String getName() { + return Messages.ProjectRepositoriesNode_name; + } + + public String toString() { + return getName(); + } + + public boolean hasChildren() { + Object[] kids = getChildren(); + return kids != null && kids.length > 0; + } + + public boolean isUpdating() { + return false; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/RepositoryNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/RepositoryNode.java new file mode 100644 index 00000000..849df8bf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/RepositoryNode.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.NexusIndex; +import org.eclipse.m2e.core.repository.IRepository; + +/** + * LocalRepsoitoryNode + * + * @author dyocum + */ +public class RepositoryNode extends AbstractIndexedRepositoryNode { + + private final IRepository repository; + + public RepositoryNode(NexusIndex index){ + super(index); + this.repository = index.getRepository(); + } + + public String getName() { + StringBuilder sb = new StringBuilder(); + sb.append(repository.getId()); + sb.append(" (").append(repository.getUrl()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (repository.getMirrorOf() != null) { + sb.append(" [mirrorOf=").append(repository.getMirrorOf()).append("]"); //$NON-NLS-2$ + } + if (repository.getMirrorId() != null) { + sb.append(" [mirrored by ").append(repository.getMirrorId()).append("]"); //$NON-NLS-2$ + } + if (isUpdating()) { + sb.append(Messages.RepositoryNode_updating); + } + return sb.toString(); + } + + public String getRepositoryUrl() { + return repository.getUrl(); + } + + public String getRepoName() { + return repository.toString(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/WorkspaceRepositoryNode.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/WorkspaceRepositoryNode.java new file mode 100644 index 00000000..4cbb19fe --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/ui/internal/views/nodes/WorkspaceRepositoryNode.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.ui.internal.views.nodes; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.internal.index.NexusIndex; + +/** + * WorkspaceRepositoryNode + * + * @author igor + */ +public class WorkspaceRepositoryNode extends AbstractIndexedRepositoryNode { + + public WorkspaceRepositoryNode(NexusIndex index) { + super(index); + } + + public String getName() { + return Messages.WorkspaceRepositoryNode_name; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EErrorDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EErrorDialog.java new file mode 100644 index 00000000..37a4a96f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EErrorDialog.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util; + + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.TableColumn; + +import org.eclipse.m2e.core.internal.Messages; + +/** + * M2EErrorDialog + * Error dialog for displaying a list/table of error values. + * + * @author dyocum + */ +public class M2EErrorDialog extends MessageDialog { + + + private TableViewer errorTable; + private static final int PROJECT_COL = 0; + protected static final int TABLE_WIDTH = 700; + protected String[] COL_NAMES = {Messages.M2EErrorDialog_column_name, Messages.M2EErrorDialog_column_error}; + protected int[] COL_STYLES = {SWT.LEFT, SWT.LEFT}; + protected Map<String, Throwable> errorMap; + + /** + * @param parentShell + * @param dialogTitle + * @param dialogTitleImage + * @param dialogMessage + * @param dialogImageType + * @param dialogButtonLabels + * @param defaultIndex + */ + public M2EErrorDialog(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, + int dialogImageType, String[] dialogButtonLabels, int defaultIndex, Map<String, Throwable> errorMap) { + super(parentShell, dialogTitle, dialogTitleImage, dialogMessage, dialogImageType, dialogButtonLabels, defaultIndex); + this.errorMap = errorMap; + setShellStyle(getShellStyle() | SWT.RESIZE); + } + + protected Control createCustomArea(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(1, true); + comp.setLayout(layout); + + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = TABLE_WIDTH+50; + gd.grabExcessHorizontalSpace=true; + gd.grabExcessVerticalSpace=true; + comp.setLayoutData(gd); + + gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = TABLE_WIDTH; + gd.heightHint = 200; + errorTable = new TableViewer(comp, SWT.BORDER|SWT.H_SCROLL|SWT.V_SCROLL|SWT.FULL_SELECTION); + errorTable.getTable().setHeaderVisible(true); + errorTable.getTable().setLinesVisible(true); + + errorTable.setContentProvider(new ErrorTableContentProvider()); + errorTable.setLabelProvider(new ErrorTableLabelProvider()); + errorTable.getControl().setLayoutData(gd); + + setupTableColumns(); + errorTable.setInput(errorMap); + return comp; + } + + /** + * Create the table columns and set up their widths + */ + protected void setupTableColumns() { + GC gc = new GC(errorTable.getControl()); + gc.setFont(errorTable.getControl().getFont()); + for(int i=0;i<COL_NAMES.length;i++){ + TableColumn col = new TableColumn(errorTable.getTable(), COL_STYLES[i]); + col.setText(COL_NAMES[i]); + int width = calcStringWidth(gc, i); + col.setWidth(width); + } + gc.dispose(); + } + + /** + * Find out how wide the strings are so the columns can be set correctly. + * @param gc + * @param column + * @return + */ + private int calcStringWidth(GC gc, int column){ + int maxWidth = 100; + if(column == PROJECT_COL){ + Set<String> keySet = errorMap.keySet(); + for(String projectName : keySet){ + int width = gc.stringExtent(projectName).x+10; + maxWidth = Math.max(maxWidth, width); + } + return maxWidth; + } + Collection<Throwable> values = errorMap.values(); + for(Throwable t : values){ + String msg = M2EUtils.getRootCauseMessage(t); + if(msg == null){ + msg = ""; //$NON-NLS-1$ + } + int width = gc.stringExtent(msg).x+10; + maxWidth = Math.max(maxWidth, width); + } + return maxWidth; + } + + /** + * ErrorTableContentProvider + * + * @author dyocum + */ + class ErrorTableContentProvider implements IStructuredContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + if(inputElement instanceof Map){ + return ((Map)inputElement).keySet().toArray(); + } + return new Object[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } + + class ErrorTableLabelProvider implements ITableLabelProvider{ + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) + */ + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) + */ + public String getColumnText(Object element, int columnIndex) { + if(columnIndex == PROJECT_COL){ + return element.toString(); + } + String msg = M2EUtils.getRootCauseMessage(errorMap.get(element)); + return msg == null ? "" : msg; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void addListener(ILabelProviderListener listener) { + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() + */ + public void dispose() { + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) + */ + public boolean isLabelProperty(Object element, String property) { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void removeListener(ILabelProviderListener listener) { + } + } + + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EUtils.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EUtils.java new file mode 100644 index 00000000..8bb6e156 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/M2EUtils.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util; + +import java.util.Map; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * M2EUtils + * + * @author dyocum + */ +public class M2EUtils { + + public static Font deriveFont(Font f, int style, int height){ + FontData[] fd = f.getFontData(); + FontData[] newFD = new FontData[fd.length]; + for(int i=0;i<fd.length;i++){ + newFD[i] = new FontData(fd[i].getName(), height, style); + } + return new Font(Display.getCurrent(), newFD); + } + + public static void showErrorDialog(Shell shell, String title, String msg, Exception e){ + StringBuffer buff = new StringBuffer(msg); + Throwable t = getRootCause(e); + if(t != null && !nullOrEmpty(t.getMessage())){ + buff.append(t.getMessage()); + } + MessageDialog.openError(shell, title, buff.toString()); + } + + public static String getRootCauseMessage(Throwable t){ + Throwable root = getRootCause(t); + if(t == null){ + return null; + } + return root.getMessage(); + } + + public static Throwable getRootCause(Throwable ex) { + if(ex == null){ + return null; + } + Throwable rootCause = ex; + Throwable cause = rootCause.getCause(); + while(cause != null && cause != rootCause) { + rootCause = cause; + cause = cause.getCause(); + } + return cause == null ? rootCause : cause; + } + + public static boolean nullOrEmpty(String s){ + return s == null || s.length() == 0; + } + + /** + * @param shell + * @param string + * @param string2 + * @param updateErrors + */ + public static void showErrorsForProjectsDialog(final Shell shell, final String title, final String message, + final Map<String, Throwable> errorMap) { + // TODO Auto-generated method showErrorsForProjectsDialog + Display.getDefault().asyncExec(new Runnable(){ + public void run(){ + String[] buttons = {IDialogConstants.OK_LABEL}; + int ok_button = 0; + M2EErrorDialog errDialog = new M2EErrorDialog(shell, title, Dialog.getImage(Dialog.DLG_IMG_MESSAGE_ERROR), message, MessageDialog.ERROR, buttons, ok_button, + errorMap); + errDialog.create(); + errDialog.open(); + } + }); + + } + + public static void addRequiredDecoration(Control control) { + FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration( + FieldDecorationRegistry.DEC_REQUIRED); + ControlDecoration controlDecoration = new ControlDecoration(control, SWT.LEFT | SWT.CENTER); + controlDecoration.setDescriptionText(fieldDecoration.getDescription()); + controlDecoration.setImage(fieldDecoration.getImage()); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/ProposalUtil.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/ProposalUtil.java new file mode 100644 index 00000000..68320167 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/ProposalUtil.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2008 Sonatype, Inc. + * 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 + *******************************************************************************/ + +package org.eclipse.m2e.core.util; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.fieldassist.IControlContentAdapter; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.util.search.CComboContentAdapter; +import org.eclipse.m2e.core.util.search.ControlDecoration; +import org.eclipse.m2e.core.util.search.Packaging; +import org.eclipse.m2e.core.util.search.SearchEngine; + + +/** + * Holds the proposal utility code, previously in the editor.xml plug-in. Provides proposal suggestions for text and + * combo widgets for various metadata (group, artifact, etc.) + * + * @author rgould + */ +public class ProposalUtil { + + public static abstract class Searcher { + public abstract Collection<String> search() throws CoreException; + } + + public static final class TextProposal implements IContentProposal { + private final String text; + + public TextProposal(String text) { + this.text = text; + } + + public int getCursorPosition() { + return text.length(); + } + + public String getContent() { + return text; + } + + public String getLabel() { + return text; + } + + public String getDescription() { + return null; + } + } + + public static void addCompletionProposal(final Control control, final Searcher searcher) { + FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration( + FieldDecorationRegistry.DEC_CONTENT_PROPOSAL); + ControlDecoration decoration = new ControlDecoration(control, SWT.LEFT | SWT.TOP); + decoration.setShowOnlyOnFocus(true); + decoration.setDescriptionText(fieldDecoration.getDescription()); + decoration.setImage(fieldDecoration.getImage()); + + IContentProposalProvider proposalProvider = new IContentProposalProvider() { + public IContentProposal[] getProposals(String contents, int position) { + ArrayList<IContentProposal> proposals = new ArrayList<IContentProposal>(); + try { + for(final String text : searcher.search()) { + proposals.add(new TextProposal(text)); + } + } catch(CoreException e) { + MavenLogger.log(e); + } + return proposals.toArray(new IContentProposal[proposals.size()]); + } + }; + + IControlContentAdapter contentAdapter; + if(control instanceof Text) { + contentAdapter = new TextContentAdapter(); + } else { + contentAdapter = new CComboContentAdapter(); + } + + ContentAssistCommandAdapter adapter = new ContentAssistCommandAdapter( // + control, contentAdapter, proposalProvider, // + ContentAssistCommandAdapter.CONTENT_PROPOSAL_COMMAND, null); + // ContentProposalAdapter adapter = new ContentProposalAdapter(control, contentAdapter, // + // proposalProvider, KeyStroke.getInstance(SWT.MOD1, ' '), null); + adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); + adapter.setPopupSize(new Point(250, 120)); + adapter.setPopupSize(new Point(250, 120)); + } + + public static void addClassifierProposal(final IProject project, final Text groupIdText, final Text artifactIdText, + final Text versionText, final Text classifierText, final Packaging packaging) { + addCompletionProposal(classifierText, new Searcher() { + public Collection<String> search() throws CoreException { + return getSearchEngine(project).findClassifiers(groupIdText.getText(), // + artifactIdText.getText(), versionText.getText(), classifierText.getText(), packaging); + } + }); + } + + public static void addVersionProposal(final IProject project, final Text groupIdText, final Text artifactIdText, + final Text versionText, final Packaging packaging) { + addCompletionProposal(versionText, new Searcher() { + public Collection<String> search() throws CoreException { + return getSearchEngine(project).findVersions(groupIdText.getText(), // + artifactIdText.getText(), versionText.getText(), packaging); + } + }); + } + + public static void addArtifactIdProposal(final IProject project, final Text groupIdText, final Text artifactIdText, + final Packaging packaging) { + addCompletionProposal(artifactIdText, new Searcher() { + public Collection<String> search() throws CoreException { + // TODO handle artifact info + return getSearchEngine(project).findArtifactIds(groupIdText.getText(), artifactIdText.getText(), packaging, + null); + } + }); + } + + public static void addGroupIdProposal(final IProject project, final Text groupIdText, final Packaging packaging) { + addCompletionProposal(groupIdText, new Searcher() { + public Collection<String> search() throws CoreException { + // TODO handle artifact info + return getSearchEngine(project).findGroupIds(groupIdText.getText(), packaging, null); + } + }); + } + + public static SearchEngine getSearchEngine(final IProject project) throws CoreException { + return MavenPlugin.getDefault().getSearchEngine(project); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/Util.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/Util.java new file mode 100644 index 00000000..fa01cbae --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/Util.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.variables.VariablesPlugin; + +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * Utility methods + * + * @author Eugene Kuleshov + */ +public class Util { + + public static boolean isEclipseVersion(int major, int minor) { + Bundle bundle = ResourcesPlugin.getPlugin().getBundle(); + String version = (String) bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION); + Version v = Version.parseVersion(version); + return v.getMajor() == major && v.getMinor() == minor; + } + + /** + * Proxy factory for compatibility stubs + */ + @SuppressWarnings("unchecked") + public static <T> T proxy(final Object o, Class<T> type) { + return (T) Proxy.newProxyInstance(type.getClassLoader(), // + new Class[] {type}, // + new InvocationHandler() { + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + try { + Method mm = o.getClass().getMethod(m.getName(), m.getParameterTypes()); + return mm.invoke(o, args); + } catch(final NoSuchMethodException e) { + return null; + } + } + }); + } + + /** + * Stub interface for FileStoreEditorInput + * + * @see Util#proxy(Object, Class) + */ + public static interface FileStoreEditorInputStub { + public java.net.URI getURI(); + } + + /** + * Helper method which creates a folder and, recursively, all its parent folders. + * + * @param folder The folder to create. + * @param derived true if folder should be marked as derived + * @throws CoreException if creating the given <code>folder</code> or any of its parents fails. + */ + public static void createFolder(IFolder folder, boolean derived) throws CoreException { + // Recurse until we find a parent folder which already exists. + if(!folder.exists()) { + IContainer parent = folder.getParent(); + // First, make sure that all parent folders exist. + if(parent != null && !parent.exists()) { + createFolder((IFolder) parent, false); + } + folder.create(true, true, null); + } + + if(folder.isAccessible() && derived) { + folder.setDerived(true); + } + } + + public static void setDerived(IFolder folder, boolean derived) throws CoreException { + if(folder.isAccessible()) { + folder.setDerived(derived); + } + } + + /** + * Substitute any variable + */ + public static String substituteVar(String s) { + if(s == null) { + return s; + } + try { + return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(s); + } catch(CoreException e) { + MavenLogger.log(e); + return null; + } + } + + public static String nvl(String s) { + return s == null ? "" : s; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ArtifactInfo.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ArtifactInfo.java new file mode 100644 index 00000000..85b90527 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ArtifactInfo.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + + +/** + * Information about the artifact. + * + * @author Lukas Krecan + */ +public class ArtifactInfo { + private final String groupId; + private final String artifactId; + private final String version; + private final String classfier; + private final String type; + + public ArtifactInfo(String groupId, String artifactId, String version, String classfier, String type) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.classfier = classfier; + this.type = type; + } + + public String getGroupId() { + return groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public String getVersion() { + return version; + } + + public String getClassfier() { + return classfier; + } + + public String getType() { + return type; + } + + /** + * Constructs a <code>String</code> with all attributes + * in name = value format. + * + * @return a <code>String</code> representation + * of this object. + */ + public String toString() + { + final String TAB = " "; //$NON-NLS-1$ + + String retValue = ""; //$NON-NLS-1$ + + retValue = "ArtifactInfo ( " //$NON-NLS-1$ + + "groupId = " + this.groupId + TAB //$NON-NLS-1$ + + "artifactId = " + this.artifactId + TAB //$NON-NLS-1$ + + "version = " + this.version + TAB //$NON-NLS-1$ + + "classfier = " + this.classfier + TAB //$NON-NLS-1$ + + "type = " + this.type + TAB //$NON-NLS-1$ + + " )"; //$NON-NLS-1$ + + return retValue; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/CComboContentAdapter.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/CComboContentAdapter.java new file mode 100644 index 00000000..2dbbe432 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/CComboContentAdapter.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.IControlContentAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Control; + + +/** + * An {@link IControlContentAdapter} for SWT CCombo controls. This is a convenience class for easily creating a + * {@link ContentProposalAdapter} for combo fields. + */ +public class CComboContentAdapter implements IControlContentAdapter /*, IControlContentAdapter2 */ { + + /* + * Set to <code>true</code> if we should compute the text + * vertical bounds rather than just use the field size. + * Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=164748 + * The corresponding SWT bug is + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=44072 + */ + private static final boolean COMPUTE_TEXT_USING_CLIENTAREA = !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$ + + public String getControlContents(Control control) { + return ((CCombo) control).getText(); + } + + public void setControlContents(Control control, String text, int cursorPosition) { + ((CCombo) control).setText(text); + ((CCombo) control).setSelection(new Point(cursorPosition, cursorPosition)); + } + + public void insertControlContents(Control control, String text, int cursorPosition) { + CCombo combo = (CCombo) control; + String contents = combo.getText(); + Point selection = combo.getSelection(); + StringBuffer sb = new StringBuffer(); + sb.append(contents.substring(0, selection.x)); + sb.append(text); + if(selection.y < contents.length()) { + sb.append(contents.substring(selection.y, contents.length())); + } + combo.setText(sb.toString()); + selection.x = selection.x + cursorPosition; + selection.y = selection.x; + combo.setSelection(selection); + } + + public int getCursorPosition(Control control) { + return ((CCombo) control).getSelection().x; + } + + public Rectangle getInsertionBounds(Control control) { + // This doesn't take horizontal scrolling into affect. + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=204599 + CCombo combo = (CCombo) control; + int position = combo.getSelection().y; + String contents = combo.getText(); + GC gc = new GC(combo); + gc.setFont(combo.getFont()); + Point extent = gc.textExtent(contents.substring(0, Math.min(position, contents.length()))); + gc.dispose(); + if(COMPUTE_TEXT_USING_CLIENTAREA) { + return new Rectangle(combo.getClientArea().x + extent.x, combo.getClientArea().y, 1, combo.getClientArea().height); + } + return new Rectangle(extent.x, 0, 1, combo.getSize().y); + } + + public void setCursorPosition(Control control, int index) { + ((CCombo) control).setSelection(new Point(index, index)); + } + + public Point getSelection(Control control) { + return ((CCombo) control).getSelection(); + } + + public void setSelection(Control control, Point range) { + ((CCombo) control).setSelection(range); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ControlDecoration.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ControlDecoration.java new file mode 100644 index 00000000..4e7b7fae --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/ControlDecoration.java @@ -0,0 +1,1088 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + + +package org.eclipse.m2e.core.util.search; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.graphics.Region; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Widget; + + +/** + * ControlDecoration renders an image decoration near a control. It allows clients to specify an image and a position + * for the image relative to the control. A ControlDecoration may be assigned description text, which can optionally be + * shown when the user hovers over the image. Clients can decorate any kind of control. + * <p> + * Decoration images always appear on the left or right side of the field, never above or below it. Decorations can be + * positioned at the top, center, or bottom of either side of the control. Future implementations may provide additional + * positioning options for decorations. + * <p> + * ControlDecoration renders the image adjacent to the specified (already created) control, with no guarantee that it + * won't be clipped or otherwise obscured or overlapped by adjacent controls, including another ControlDecoration placed + * in the same location. Clients should ensure that there is adequate space adjacent to the control to show the + * decoration properly. + * <p> + * Clients using ControlDecoration should typically ensure that enough margin space is reserved for a decoration by + * altering the layout data margins, although this is not assumed or required by the ControlDecoration implementation. + * <p> + * This class is intended to be instantiated and used by clients. It is not intended to be subclassed by clients. + * + * @since 3.3 + * @see FieldDecoration + * @see FieldDecorationRegistry + */ +public class ControlDecoration { + /** + * Debug flag for tracing + */ + private static boolean DEBUG = false; + + /** + * Cached platform flags for dealing with platform-specific issues. + */ + static boolean CARBON = "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$ + + /** + * The associated control + */ + Control control; + + /** + * The composite on which to render the decoration and hook mouse events, or null if we are hooking all parent + * composites. + */ + private Composite composite; + + /** + * The associated image. + */ + private Image image; + + /** + * The associated description text. + */ + private String descriptionText; + + /** + * The position of the decoration. + */ + private int position; + + /** + * The decoration's visibility flag + */ + boolean visible = true; + + /** + * Boolean indicating whether the decoration should only be shown when the control has focus + */ + boolean showOnlyOnFocus = false; + + /** + * Boolean indicating whether the decoration should show its description text in a hover when the user hovers over the + * decoration. + */ + boolean showHover = true; + + /** + * Margin width used between the decorator and the control. + */ + private int marginWidth = 0; + + /** + * Registered selection listeners. + */ + ListenerList selectionListeners = new ListenerList(); + + /** + * Registered menu detect listeners. + */ + ListenerList menuDetectListeners = new ListenerList(); + + /** + * The focus listener + */ + private FocusListener focusListener; + + /** + * The dispose listener + */ + private DisposeListener disposeListener; + + /** + * The paint listener installed for drawing the decoration + */ + private PaintListener paintListener; + + /** + * The mouse listener installed for tracking the hover + */ + private MouseTrackListener mouseTrackListener; + + /** + * The mouse move listener installed for tracking the hover + */ + MouseMoveListener mouseMoveListener; + + /** + * The untyped listener installed for notifying external listeners + */ + private Listener compositeListener; + + /** + * Control that we last installed a move listener on. We only want one at a time. + */ + Control moveListeningTarget = null; + + /** + * Debug counter used to match add and remove listeners + */ + private int listenerInstalls = 0; + + /** + * The current rectangle used for tracking mouse moves + */ + Rectangle decorationRectangle; + + /** + * An internal flag tracking whether we have focus. We use this rather than isFocusControl() so that we can set the + * flag as soon as we get the focus callback, rather than having to do an asyncExec in the middle of a focus callback + * to ensure that isFocusControl() represents the outcome of the event. + */ + boolean hasFocus = false; + + /** + * The hover used for showing description text + */ + private Hover hover; + + /** + * The hover used to show a decoration image's description. + */ + class Hover { + private static final String EMPTY = ""; //$NON-NLS-1$ + + /** + * Offset of info hover arrow from the left or right side. + */ + private int hao = 10; + + /** + * Width of info hover arrow. + */ + private int haw = 8; + + /** + * Height of info hover arrow. + */ + private int hah = 10; + + /** + * Margin around info hover text. + */ + int hm = 2; + + /** + * This info hover's shell. + */ + Shell hoverShell; + + /** + * The info hover text. + */ + String text = EMPTY; + + /** + * The region used to manage the shell shape + */ + Region region; + + /** + * Boolean indicating whether the last computed polygon location had an arrow on left. (true if left, false if + * right). + */ + boolean arrowOnLeft = true; + + /* + * Create a hover parented by the specified shell. + */ + Hover(Shell parent) { + final Display display = parent.getDisplay(); + hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL); + hoverShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + hoverShell.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); + hoverShell.addPaintListener(new PaintListener() { + public void paintControl(PaintEvent pe) { + pe.gc.drawText(text, hm, hm); + if(!CARBON) { + pe.gc.drawPolygon(getPolygon(true)); + } + } + }); + hoverShell.addMouseListener(new MouseAdapter() { + public void mouseDown(MouseEvent e) { + hideHover(); + } + }); + } + + /* + * Compute a polygon that represents a hover with an arrow pointer. If + * border is true, compute the polygon inset by 1-pixel border. Consult + * the arrowOnLeft flag to determine which side the arrow is on. + */ + int[] getPolygon(boolean border) { + Point e = getExtent(); + int b = border ? 1 : 0; + if(arrowOnLeft) { + return new int[] {0, 0, e.x - b, 0, e.x - b, e.y - b, hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao, + e.y - b, 0, e.y - b, 0, 0}; + } + return new int[] {0, 0, e.x - b, 0, e.x - b, e.y - b, e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b, + e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0}; + } + + /* + * Dispose the hover, it is no longer needed. Dispose any resources + * allocated by the hover. + */ + void dispose() { + if(!hoverShell.isDisposed()) { + hoverShell.dispose(); + } + if(region != null) { + region.dispose(); + } + } + + /* + * Set the visibility of the hover. + */ + void setVisible(boolean visible) { + if(visible) { + if(!hoverShell.isVisible()) { + hoverShell.setVisible(true); + } + } else { + if(hoverShell.isVisible()) { + hoverShell.setVisible(false); + } + } + } + + /* + * Set the text of the hover to the specified text. Recompute the size + * and location of the hover to hover near the decoration rectangle, + * pointing the arrow toward the target control. + */ + void setText(String t, Rectangle decorationRectangle, Control targetControl) { + if(t == null) { + t = EMPTY; + } + if(!t.equals(text)) { + Point oldSize = getExtent(); + text = t; + hoverShell.redraw(); + Point newSize = getExtent(); + if(!oldSize.equals(newSize)) { + // set a flag that indicates the direction of arrow + arrowOnLeft = decorationRectangle.x <= targetControl.getLocation().x; + setNewShape(); + } + } + + Point extent = getExtent(); + int y = -extent.y - hah + 1; + int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2; + + hoverShell.setLocation(control.getParent().toDisplay(decorationRectangle.x + x, decorationRectangle.y + y)); + } + + /* + * Return whether or not the hover (shell) is visible. + */ + boolean isVisible() { + return hoverShell.isVisible(); + } + + /* + * Compute the extent of the hover for the current text. + */ + Point getExtent() { + GC gc = new GC(hoverShell); + Point e = gc.textExtent(text); + gc.dispose(); + e.x += hm * 2; + e.y += hm * 2; + return e; + } + + /* + * Compute a new shape for the hover shell. + */ + void setNewShape() { + Region oldRegion = region; + region = new Region(); + region.add(getPolygon(false)); + hoverShell.setRegion(region); + if(oldRegion != null) { + oldRegion.dispose(); + } + + } + } + + /** + * Construct a ControlDecoration for decorating the specified control at the specified position relative to the + * control. Render the decoration on top of any Control that happens to appear at the specified location. + * <p> + * SWT constants are used to specify the position of the decoration relative to the control. The position should + * include style bits describing both the vertical and horizontal orientation. <code>SWT.LEFT</code> and + * <code>SWT.RIGHT</code> describe the horizontal placement of the decoration relative to the control, and the + * constants <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and <code>SWT.BOTTOM</code> describe the vertical + * alignment of the decoration relative to the control. Decorations always appear on either the left or right side of + * the control, never above or below it. For example, a decoration appearing on the left side of the field, at the + * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are specified, the control decoration will be + * positioned to the left and center of the control (<code>SWT.LEFT | SWT.CENTER</code>). + * </p> + * + * @param control the control to be decorated + * @param position bit-wise or of position constants (<code>SWT.TOP</code>, <code>SWT.BOTTOM</code>, + * <code>SWT.LEFT</code>, <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>). + */ + public ControlDecoration(Control control, int position) { + this(control, position, null); + + } + + /** + * Construct a ControlDecoration for decorating the specified control at the specified position relative to the + * control. Render the decoration only on the specified Composite or its children. The decoration will be clipped if + * it does not appear within the visible bounds of the composite or its child composites. + * <p> + * SWT constants are used to specify the position of the decoration relative to the control. The position should + * include style bits describing both the vertical and horizontal orientation. <code>SWT.LEFT</code> and + * <code>SWT.RIGHT</code> describe the horizontal placement of the decoration relative to the control, and the + * constants <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and <code>SWT.BOTTOM</code> describe the vertical + * alignment of the decoration relative to the control. Decorations always appear on either the left or right side of + * the control, never above or below it. For example, a decoration appearing on the left side of the field, at the + * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are specified, the control decoration will be + * positioned to the left and center of the control (<code>SWT.LEFT | SWT.CENTER</code>). + * </p> + * + * @param control the control to be decorated + * @param position bit-wise or of position constants (<code>SWT.TOP</code>, <code>SWT.BOTTOM</code>, + * <code>SWT.LEFT</code>, <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>). + * @param composite The SWT composite within which the decoration should be rendered. The decoration will be clipped + * to this composite, but it may be rendered on a child of the composite. The decoration will not be visible + * if the specified composite or its child composites are not visible in the space relative to the control, + * where the decoration is to be rendered. If this value is <code>null</code>, then the decoration will be + * rendered on whichever composite (or composites) are located in the specified position. + */ + public ControlDecoration(Control control, int position, Composite composite) { + this.position = position; + this.control = control; + this.composite = composite; + + addControlListeners(); + + } + + /** + * Adds the listener to the collection of listeners who will be notified when the platform-specific context menu + * trigger has occurred, by sending it one of the messages defined in the <code>MenuDetectListener</code> interface. + * <p> + * The <code>widget</code> field in the SelectionEvent will contain the Composite on which the decoration is rendered + * that received the click. The <code>x</code> and <code>y</code> fields will be in coordinates relative to the + * display. The <code>data</code> field will contain the decoration that received the event. + * </p> + * + * @param listener the listener which should be notified + * @see org.eclipse.swt.events.MenuDetectListener + * @see org.eclipse.swt.events.MenuDetectEvent + * @see #removeMenuDetectListener + */ + public void addMenuDetectListener(MenuDetectListener listener) { + menuDetectListeners.add(listener); + } + + /** + * Removes the listener from the collection of listeners who will be notified when the platform-specific context menu + * trigger has occurred. + * + * @param listener the listener which should no longer be notified. This message has no effect if the listener was not + * previously added to the receiver. + * @see org.eclipse.swt.events.MenuDetectListener + * @see #addMenuDetectListener + */ + public void removeMenuDetectListener(MenuDetectListener listener) { + menuDetectListeners.remove(listener); + } + + /** + * Adds the listener to the collection of listeners who will be notified when the decoration is selected, by sending + * it one of the messages defined in the <code>SelectionListener</code> interface. + * <p> + * <code>widgetSelected</code> is called when the decoration is selected (by mouse click). + * <code>widgetDefaultSelected</code> is called when the decoration is double-clicked. + * </p> + * <p> + * The <code>widget</code> field in the SelectionEvent will contain the Composite on which the decoration is rendered + * that received the click. The <code>x</code> and <code>y</code> fields will be in coordinates relative to that + * widget. The <code>data</code> field will contain the decoration that received the event. + * </p> + * + * @param listener the listener which should be notified + * @see org.eclipse.swt.events.SelectionListener + * @see org.eclipse.swt.events.SelectionEvent + * @see #removeSelectionListener + */ + public void addSelectionListener(SelectionListener listener) { + selectionListeners.add(listener); + } + + /** + * Removes the listener from the collection of listeners who will be notified when the decoration is selected. + * + * @param listener the listener which should no longer be notified. This message has no effect if the listener was not + * previously added to the receiver. + * @see org.eclipse.swt.events.SelectionListener + * @see #addSelectionListener + */ + public void removeSelectionListener(SelectionListener listener) { + selectionListeners.remove(listener); + } + + /** + * Dispose this ControlDecoration. Unhook any listeners that have been installed on the target control. This method + * has no effect if the receiver is already disposed. + */ + public void dispose() { + if(control == null) { + return; + } + if(hover != null) { + hover.dispose(); + hover = null; + } + removeControlListeners(); + control = null; + } + + /** + * Get the control that is decorated by the receiver. + * + * @return the Control decorated by the receiver. May be <code>null</code> if the control has been uninstalled. + */ + public Control getControl() { + return control; + } + + /** + * Add any listeners needed on the target control and on the composite where the decoration is to be rendered. + */ + private void addControlListeners() { + disposeListener = new DisposeListener() { + public void widgetDisposed(DisposeEvent event) { + dispose(); + } + }; + printAddListener(control, "DISPOSE"); //$NON-NLS-1$ + control.addDisposeListener(disposeListener); + + focusListener = new FocusListener() { + public void focusGained(FocusEvent event) { + hasFocus = true; + if(showOnlyOnFocus) { + update(); + } + } + + public void focusLost(FocusEvent event) { + hasFocus = false; + if(showOnlyOnFocus) { + update(); + } + } + }; + printAddListener(control, "FOCUS"); //$NON-NLS-1$ + control.addFocusListener(focusListener); + + // Listener for painting the decoration + paintListener = new PaintListener() { + public void paintControl(PaintEvent event) { + Control control = (Control) event.widget; + Rectangle rect = getDecorationRectangle(control); + if(shouldShowDecoration()) { + event.gc.drawImage(getImage(), rect.x, rect.y); + } + } + }; + + // Listener for tracking the end of a hover. Only installed + // after a hover begins. + mouseMoveListener = new MouseMoveListener() { + public void mouseMove(MouseEvent event) { + if(showHover) { + if(!decorationRectangle.contains(event.x, event.y)) { + hideHover(); + // No need to listen any longer + printRemoveListener(event.widget, "MOUSEMOVE"); //$NON-NLS-1$ + ((Control) event.widget).removeMouseMoveListener(mouseMoveListener); + moveListeningTarget = null; + } + } + } + }; + + // Listener for tracking the beginning of a hover. Always installed. + mouseTrackListener = new MouseTrackListener() { + public void mouseExit(MouseEvent event) { + // Just in case we didn't catch it before. + Control target = (Control) event.widget; + if(target == moveListeningTarget) { + printRemoveListener(target, "MOUSEMOVE"); //$NON-NLS-1$ + target.removeMouseMoveListener(mouseMoveListener); + moveListeningTarget = null; + } + hideHover(); + } + + public void mouseHover(MouseEvent event) { + if(showHover) { + decorationRectangle = getDecorationRectangle((Control) event.widget); + if(decorationRectangle.contains(event.x, event.y)) { + showHoverText(getDescriptionText()); + Control target = (Control) event.widget; + if(moveListeningTarget == null) { + printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$ + target.addMouseMoveListener(mouseMoveListener); + moveListeningTarget = target; + } else if(target != moveListeningTarget) { + printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$ + moveListeningTarget.removeMouseMoveListener(mouseMoveListener); + printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$ + target.addMouseMoveListener(mouseMoveListener); + moveListeningTarget = target; + } else { + // It is already installed on this control. + } + } + } + } + + public void mouseEnter(MouseEvent event) { + // Nothing to do until a hover occurs. + } + }; + + compositeListener = new Listener() { + public void handleEvent(Event event) { + // Don't forward events if decoration is not showing + if(!visible) { + return; + } + // Notify listeners if any are registered. + switch(event.type) { + case SWT.MouseDown: + if(!selectionListeners.isEmpty()) + notifySelectionListeners(event); + break; + case SWT.MouseDoubleClick: + if(!selectionListeners.isEmpty()) + notifySelectionListeners(event); + break; + case SWT.MenuDetect: + if(!menuDetectListeners.isEmpty()) + notifyMenuDetectListeners(event); + break; + } + } + }; + + // We do not know which parent in the control hierarchy + // is providing the decoration space, so hook all the way up, until + // the shell or the specified parent composite is reached. + Composite c = control.getParent(); + while(c != null) { + installCompositeListeners(c); + if(composite != null && composite == c) { + // We just installed on the specified composite, so stop. + c = null; + } else if(c instanceof Shell) { + // We just installed on a shell, so don't go further + c = null; + } else { + c = c.getParent(); + } + } + // force a redraw of the decoration area so our paint listener + // is notified. + update(); + } + + /* + * Install the listeners used to paint and track mouse events on the + * composite. + */ + private void installCompositeListeners(Composite c) { + if(!c.isDisposed()) { + printAddListener(c, "PAINT"); //$NON-NLS-1$ + c.addPaintListener(paintListener); + printAddListener(c, "MOUSETRACK"); //$NON-NLS-1$ + c.addMouseTrackListener(mouseTrackListener); + printAddListener(c, "SWT.MenuDetect"); //$NON-NLS-1$ + c.addListener(SWT.MenuDetect, compositeListener); + printAddListener(c, "SWT.MouseDown"); //$NON-NLS-1$ + c.addListener(SWT.MouseDown, compositeListener); + printAddListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$ + c.addListener(SWT.MouseDoubleClick, compositeListener); + } + } + + /* + * Remove the listeners used to paint and track mouse events on the + * composite. + */ + private void removeCompositeListeners(Composite c) { + if(!c.isDisposed()) { + printRemoveListener(c, "PAINT"); //$NON-NLS-1$ + c.removePaintListener(paintListener); + printRemoveListener(c, "MOUSETRACK"); //$NON-NLS-1$ + c.removeMouseTrackListener(mouseTrackListener); + printRemoveListener(c, "SWT.MenuDetect"); //$NON-NLS-1$ + c.removeListener(SWT.MenuDetect, compositeListener); + printRemoveListener(c, "SWT.MouseDown"); //$NON-NLS-1$ + c.removeListener(SWT.MouseDown, compositeListener); + printRemoveListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$ + c.removeListener(SWT.MouseDoubleClick, compositeListener); + } + } + + void notifySelectionListeners(Event event) { + if(!(event.widget instanceof Control)) { + return; + } + if(getDecorationRectangle((Control) event.widget).contains(event.x, event.y)) { + SelectionEvent clientEvent = new SelectionEvent(event); + clientEvent.data = this; + if(getImage() != null) { + clientEvent.height = getImage().getBounds().height; + clientEvent.width = getImage().getBounds().width; + } + Object[] listeners; + switch(event.type) { + case SWT.MouseDoubleClick: + if(event.button == 1) { + listeners = selectionListeners.getListeners(); + for(int i = 0; i < listeners.length; i++ ) { + ((SelectionListener) listeners[i]).widgetDefaultSelected(clientEvent); + } + } + break; + case SWT.MouseDown: + if(event.button == 1) { + listeners = selectionListeners.getListeners(); + for(int i = 0; i < listeners.length; i++ ) { + ((SelectionListener) listeners[i]).widgetSelected(clientEvent); + } + } + break; + } + } + } + + void notifyMenuDetectListeners(Event event) { + if(getDecorationRectangle(null).contains(event.x, event.y)) { + MenuDetectEvent clientEvent = new MenuDetectEvent(event); + clientEvent.data = this; + Object[] listeners = menuDetectListeners.getListeners(); + for(int i = 0; i < listeners.length; i++ ) { + ((MenuDetectListener) listeners[i]).menuDetected(clientEvent); + + } + } + } + + /** + * Show the specified text using the same hover dialog as is used to show decorator descriptions. When + * {@link #setShowHover(boolean)} has been set to <code>true</code>, a decoration's description text will be shown in + * an info hover over the field's control whenever the mouse hovers over the decoration. This method can be used to + * show a decoration's description text at other times (such as when the control receives focus), or to show other + * text associated with the field. The hover will not be shown if the decoration is hidden. + * + * @param text the text to be shown in the info hover, or <code>null</code> if no text should be shown. + */ + public void showHoverText(String text) { + if(control == null) { + return; + } + showHoverText(text, control); + } + + /** + * Hide any hover popups that are currently showing on the control. When {@link #setShowHover(boolean)} has been set + * to <code>true</code>, a decoration's description text will be shown in an info hover over the field's control as + * long as the mouse hovers over the decoration, and will be hidden when the mouse exits the decoration. This method + * can be used to hide a hover, whether it was shown explicitly using {@link #showHoverText(String)}, or was showing + * because the user was hovering in the decoration. + * <p> + * This message has no effect if there is no current hover. + */ + public void hideHover() { + if(hover != null) { + hover.setVisible(false); + } + } + + /** + * Show the control decoration. This message has no effect if the decoration is already showing. If + * {@link #setShowOnlyOnFocus(boolean)} is set to <code>true</code>, the decoration will only be shown if the control + * has focus. + */ + public void show() { + if(!visible) { + visible = true; + update(); + } + } + + /** + * Hide the control decoration and any associated hovers. This message has no effect if the decoration is already + * hidden. + */ + public void hide() { + if(visible) { + visible = false; + hideHover(); + update(); + } + } + + /** + * Get the description text that may be shown in a hover for this decoration. + * + * @return the text to be shown as a description for the decoration, or <code>null</code> if none has been set. + */ + public String getDescriptionText() { + return descriptionText; + } + + /** + * Set the image shown in this control decoration. Update the rendered decoration. + * + * @param text the text to be shown as a description for the decoration, or <code>null</code> if none has been set. + */ + public void setDescriptionText(String text) { + this.descriptionText = text; + update(); + } + + /** + * Get the image shown in this control decoration. + * + * @return the image to be shown adjacent to the control, or <code>null</code> if one has not been set. + */ + public Image getImage() { + return image; + } + + /** + * Set the image shown in this control decoration. Update the rendered decoration. + * + * @param image the image to be shown adjacent to the control. Should never be <code>null</code>. + */ + public void setImage(Image image) { + this.image = image; + update(); + } + + /** + * Get the boolean that controls whether the decoration is shown only when the control has focus. The default value of + * this setting is <code>false</code>. + * + * @return <code>true</code> if the decoration should only be shown when the control has focus, and <code>false</code> + * if it should always be shown. Note that if the control is not capable of receiving focus ( + * <code>SWT.NO_FOCUS</code>), then the decoration will never show when this value is <code>true</code>. + */ + public boolean getShowOnlyOnFocus() { + return showOnlyOnFocus; + } + + /** + * Set the boolean that controls whether the decoration is shown only when the control has focus. The default value of + * this setting is <code>false</code>. + * + * @param showOnlyOnFocus <code>true</code> if the decoration should only be shown when the control has focus, and + * <code>false</code> if it should always be shown. Note that if the control is not capable of receiving + * focus (<code>SWT.NO_FOCUS</code>), then the decoration will never show when this value is + * <code>true</code>. + */ + public void setShowOnlyOnFocus(boolean showOnlyOnFocus) { + this.showOnlyOnFocus = showOnlyOnFocus; + update(); + } + + /** + * Get the boolean that controls whether the decoration's description text should be shown in a hover when the user + * hovers over the decoration. The default value of this setting is <code>true</code>. + * + * @return <code>true</code> if a hover popup containing the decoration's description text should be shown when the + * user hovers over the decoration, and <code>false</code> if a hover should not be shown. + */ + public boolean getShowHover() { + return showHover; + } + + /** + * Set the boolean that controls whether the decoration's description text should be shown in a hover when the user + * hovers over the decoration. The default value of this setting is <code>true</code>. + * + * @param showHover <code>true</code> if a hover popup containing the decoration's description text should be shown + * when the user hovers over the decoration, and <code>false</code> if a hover should not be shown. + */ + public void setShowHover(boolean showHover) { + this.showHover = showHover; + update(); + } + + /** + * Get the margin width in pixels that should be used between the decorator and the horizontal edge of the control. + * The default value of this setting is <code>0</code>. + * + * @return the number of pixels that should be reserved between the horizontal edge of the control and the adjacent + * edge of the decoration. + */ + public int getMarginWidth() { + return marginWidth; + } + + /** + * Set the margin width in pixels that should be used between the decorator and the horizontal edge of the control. + * The default value of this setting is <code>0</code>. + * + * @param marginWidth the number of pixels that should be reserved between the horizontal edge of the control and the + * adjacent edge of the decoration. + */ + public void setMarginWidth(int marginWidth) { + this.marginWidth = marginWidth; + update(); + } + + /** + * Something has changed, requiring redraw. Redraw the decoration and update the hover text if appropriate. + */ + protected void update() { + if(control == null || control.isDisposed()) { + return; + } + Rectangle rect = getDecorationRectangle(control.getShell()); + // Redraw this rectangle in all children + control.getShell().redraw(rect.x, rect.y, rect.width, rect.height, true); + control.getShell().update(); + if(hover != null && getDescriptionText() != null) { + hover.setText(getDescriptionText(), getDecorationRectangle(control.getParent()), control); + } + } + + /* + * Show the specified text in the hover, positioning the hover near the + * specified control. + */ + private void showHoverText(String text, Control hoverNear) { + // If we aren't to show a hover, don't do anything. + if(!showHover) { + return; + } + + // If we are not visible, don't show the hover. + if(!visible) { + return; + } + // If there is no text, don't do anything. + if(text == null) { + hideHover(); + return; + } + + // If there is no control, nothing to do + if(control == null) { + return; + } + // Create the hover if it's not showing + if(hover == null) { + hover = new Hover(hoverNear.getShell()); + } + hover.setText(text, getDecorationRectangle(control.getParent()), control); + hover.setVisible(true); + } + + /* + * Remove any listeners installed on the controls. + */ + private void removeControlListeners() { + if(control == null) { + return; + } + printRemoveListener(control, "FOCUS"); //$NON-NLS-1$ + control.removeFocusListener(focusListener); + focusListener = null; + + printRemoveListener(control, "DISPOSE"); //$NON-NLS-1$ + control.removeDisposeListener(disposeListener); + disposeListener = null; + + Composite c = control.getParent(); + while(c != null) { + removeCompositeListeners(c); + if(composite != null && composite == c) { + // We previously installed listeners only to the specified + // composite, so stop. + c = null; + } else if(c instanceof Shell) { + // We previously installed listeners only up to the first Shell + // encountered, so stop. + c = null; + } else { + c = c.getParent(); + } + } + paintListener = null; + mouseTrackListener = null; + compositeListener = null; + + // We may have a remaining mouse move listener installed + if(moveListeningTarget != null) { + printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$ + moveListeningTarget.removeMouseMoveListener(mouseMoveListener); + moveListeningTarget = null; + mouseMoveListener = null; + } + if(DEBUG) { + if(listenerInstalls > 0) { + System.out.println("LISTENER LEAK>>>CHECK TRACE ABOVE"); //$NON-NLS-1$ + } else if(listenerInstalls < 0) { + System.out.println("REMOVED UNREGISTERED LISTENERS>>>CHECK TRACE ABOVE"); //$NON-NLS-1$ + } else { + System.out.println("ALL INSTALLED LISTENERS WERE REMOVED."); //$NON-NLS-1$ + } + } + } + + /** + * Return the rectangle in which the decoration should be rendered, in coordinates relative to the specified control. + * If the specified control is null, return the rectangle in display coordinates. + * + * @param targetControl the control whose coordinates should be used + * @return the rectangle in which the decoration should be rendered + */ + protected Rectangle getDecorationRectangle(Control targetControl) { + if(getImage() == null || control == null) { + return new Rectangle(0, 0, 0, 0); + } + // Compute the bounds first relative to the control's parent. + Rectangle imageBounds = getImage().getBounds(); + Rectangle controlBounds = control.getBounds(); + int x, y; + // Compute x + if((position & SWT.RIGHT) == SWT.RIGHT) { + x = controlBounds.x + controlBounds.width + marginWidth; + } else { + // default is left + x = controlBounds.x - imageBounds.width - marginWidth; + } + // Compute y + if((position & SWT.TOP) == SWT.TOP) { + y = controlBounds.y; + } else if((position & SWT.BOTTOM) == SWT.BOTTOM) { + y = controlBounds.y + control.getBounds().height - imageBounds.height; + } else { + // default is center + y = controlBounds.y + (control.getBounds().height - imageBounds.height) / 2; + } + + // Now convert to coordinates relative to the target control. + Point globalPoint = control.getParent().toDisplay(x, y); + Point targetPoint; + if(targetControl == null) { + targetPoint = globalPoint; + } else { + targetPoint = targetControl.toControl(globalPoint); + } + return new Rectangle(targetPoint.x, targetPoint.y, imageBounds.width, imageBounds.height); + } + + /* + * Return true if the decoration should be shown, false if it should not. + */ + boolean shouldShowDecoration() { + if(!visible) { + return false; + } + if(control == null || control.isDisposed() || getImage() == null) { + return false; + } + + if(!control.isVisible()) { + return false; + } + if(showOnlyOnFocus) { + return hasFocus; + } + return true; + } + + /* + * If in debug mode, print info about adding the specified listener. + */ + void printAddListener(Widget widget, String listenerType) { + listenerInstalls++ ; + if(DEBUG) { + System.out.println("Added listener>>>" + listenerType + " to>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$ + } + } + + /* + * If in debug mode, print info about adding the specified listener. + */ + void printRemoveListener(Widget widget, String listenerType) { + listenerInstalls-- ; + if(DEBUG) { + System.out.println("Removed listener>>>" + listenerType + " from>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$ + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/IndexSearchEngine.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/IndexSearchEngine.java new file mode 100644 index 00000000..6ac3717b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/IndexSearchEngine.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.eclipse.core.runtime.CoreException; + +import org.apache.maven.artifact.versioning.ComparableVersion; + +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.MatchTyped.MatchType; +import org.eclipse.m2e.core.index.MatchTypedStringSearchExpression; +import org.eclipse.m2e.core.index.SearchExpression; + + +/** + * Search engine integrating {@link IndexManager} with POM XML editor. + * + * @author Lukas Krecan + * @author Eugene Kuleshov + */ +public class IndexSearchEngine implements SearchEngine { + + private final IIndex index; + + public IndexSearchEngine(IIndex index) { + this.index = index; + } + + protected boolean isBlank(String str) { + return str == null || str.trim().length() == 0; + } + + public Collection<String> findArtifactIds(String groupId, String searchExpression, Packaging packaging, + ArtifactInfo containingArtifact) { + // TODO add support for implicit groupIds in plugin dependencies "org.apache.maven.plugins", ... + // Someone, give me here access to settings.xml, to be able to pick up "real" predefined groupIds added by user + // Currently, I am just simulating the "factory defaults" of maven, but user changes to settings.xml + // will not be picked up this way! + ArrayList<SearchExpression> groupIdSearchExpressions = new ArrayList<SearchExpression>(); + if(isBlank(groupId)) { + // values from effective settings + // we are wiring in the defaults only, but user changes are lost! + // org.apache.maven.plugins + // org.codehaus.mojo + groupIdSearchExpressions.add(new MatchTypedStringSearchExpression("org.apache.maven.plugins", MatchType.EXACT)); + groupIdSearchExpressions.add(new MatchTypedStringSearchExpression("org.codehaus.mojo", MatchType.EXACT)); + } else { + groupIdSearchExpressions.add(new MatchTypedStringSearchExpression(groupId, MatchType.EXACT)); + } + + try { + TreeSet<String> ids = new TreeSet<String>(); + for(IndexedArtifact artifact : index.find(groupIdSearchExpressions, null, null, + Collections.singleton(packaging.toSearchExpression()))) { + ids.add(artifact.getArtifactId()); + } + return subSet(ids, searchExpression); + } catch(CoreException ex) { + throw new SearchException(ex.getMessage(), ex.getStatus().getException()); + } + } + + public Collection<String> findClassifiers(String groupId, String artifactId, String version, String prefix, + Packaging packaging) { + try { + Collection<IndexedArtifact> values = index.find(new MatchTypedStringSearchExpression(groupId, MatchType.EXACT), + new MatchTypedStringSearchExpression(artifactId, MatchType.EXACT), null, packaging.toSearchExpression()); + if(values.isEmpty()) { + return Collections.emptySet(); + } + + TreeSet<String> ids = new TreeSet<String>(); + Set<IndexedArtifactFile> files = values.iterator().next().getFiles(); + for(IndexedArtifactFile artifactFile : files) { + if(artifactFile.classifier != null) { + ids.add(artifactFile.classifier); + } + } + return subSet(ids, prefix); + } catch(CoreException ex) { + throw new SearchException(ex.getMessage(), ex.getStatus().getException()); + } + } + + public Collection<String> findGroupIds(String searchExpression, Packaging packaging, ArtifactInfo containingArtifact) { + try { + TreeSet<String> ids = new TreeSet<String>(); + + SearchExpression groupSearchExpression = isBlank(searchExpression) ? null : new MatchTypedStringSearchExpression( + searchExpression, MatchType.PARTIAL); + + for(IndexedArtifact artifact : index.find(groupSearchExpression, null, null, packaging.toSearchExpression())) { + ids.add(artifact.getGroupId()); + } + return subSet(ids, searchExpression); + } catch(CoreException ex) { + throw new SearchException(ex.getMessage(), ex.getStatus().getException()); + } + } + + public Collection<String> findTypes(String groupId, String artifactId, String version, String prefix, + Packaging packaging) { + try { + Collection<IndexedArtifact> values = index.find(new MatchTypedStringSearchExpression(groupId, MatchType.EXACT), + new MatchTypedStringSearchExpression(artifactId, MatchType.EXACT), null, packaging.toSearchExpression()); + if(values.isEmpty()) { + return Collections.emptySet(); + } + + TreeSet<String> ids = new TreeSet<String>(); + Set<IndexedArtifactFile> files = values.iterator().next().getFiles(); + for(IndexedArtifactFile artifactFile : files) { + if(artifactFile.type != null) { + ids.add(artifactFile.type); + } + } + return subSet(ids, prefix); + } catch(CoreException ex) { + throw new SearchException(ex.getMessage(), ex.getStatus().getException()); + } + } + + public Collection<String> findVersions(String groupId, String artifactId, String searchExpression, Packaging packaging) { + try { + Collection<IndexedArtifact> values = index.find(new MatchTypedStringSearchExpression(groupId, MatchType.EXACT), + new MatchTypedStringSearchExpression(artifactId, MatchType.EXACT), null, packaging.toSearchExpression()); + if(values.isEmpty()) { + return Collections.emptySet(); + } + + TreeSet<String> ids = new TreeSet<String>(); + Set<IndexedArtifactFile> files = values.iterator().next().getFiles(); + for(IndexedArtifactFile artifactFile : files) { + ids.add(artifactFile.version); + } + Collection<String> result = subSet(ids, searchExpression); + + // sort results according to o.a.m.artifact.versioning.ComparableVersion + SortedSet<ComparableVersion> versions = new TreeSet<ComparableVersion>(); + for(String version : result) { + versions.add(new ComparableVersion(version)); + } + result = null; // not used any more + List<String> sorted = new ArrayList<String>(versions.size()); + for(ComparableVersion version : versions) { + sorted.add(version.toString()); + } + versions = null; // not used any more + Collections.reverse(sorted); + return sorted; + } catch(CoreException ex) { + throw new SearchException(ex.getMessage(), ex.getStatus().getException()); + } + } + + private Collection<String> subSet(TreeSet<String> ids, String searchExpression) { + if(searchExpression == null || searchExpression.length() == 0) { + return ids; + } + int n = searchExpression.length(); + return ids.subSet(searchExpression, // + searchExpression.substring(0, n - 1) + ((char) (searchExpression.charAt(n - 1) + 1))); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectEvent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectEvent.java new file mode 100644 index 00000000..95d976b5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectEvent.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + + +import org.eclipse.swt.events.TypedEvent; +import org.eclipse.swt.widgets.Event; + +/** + * Instances of this class are sent whenever the platform- + * specific trigger for showing a context menu is detected. + * + * @see MenuDetectListener + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + */ + +public final class MenuDetectEvent extends TypedEvent { + + /** + * the display-relative x coordinate of the pointer + * at the time the context menu trigger occurred + */ + public int x; + + /** + * the display-relative y coordinate of the pointer + * at the time the context menu trigger occurred + */ + public int y; + + /** + * A flag indicating whether the operation should be allowed. + * Setting this field to <code>false</code> will cancel the operation. + */ + public boolean doit; + + private static final long serialVersionUID = -3061660596590828941L; + +/** + * Constructs a new instance of this class based on the + * information in the given untyped event. + * + * @param e the untyped event containing the information + */ +public MenuDetectEvent(Event e) { + super(e); + this.x = e.x; + this.y = e.y; + this.doit = e.doit; +} + +/** + * Returns a string containing a concise, human-readable + * description of the receiver. + * + * @return a string representation of the event + */ +public String toString() { + String string = super.toString (); + return string.substring (0, string.length() - 1) // remove trailing '}' + + " x=" + x //$NON-NLS-1$ + + " y=" + y //$NON-NLS-1$ + + " doit=" + doit //$NON-NLS-1$ + + "}"; //$NON-NLS-1$ +} +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectListener.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectListener.java new file mode 100644 index 00000000..4de16ce7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/MenuDetectListener.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + + +import java.util.EventListener; + +/** + * Classes which implement this interface provide methods + * that deal with the events that are generated when the + * platform-specific trigger for showing a context menu is + * detected. + * <p> + * After creating an instance of a class that implements + * this interface it can be added to a control or TrayItem + * using the <code>addMenuDetectListener</code> method and + * removed using the <code>removeMenuDetectListener</code> method. + * When the context menu trigger occurs, the + * <code>menuDetected</code> method will be invoked. + * </p> + * + * @see MenuDetectEvent + * + * @since 3.3 + */ +public interface MenuDetectListener extends EventListener { + +/** + * Sent when the platform-dependent trigger for showing a menu item is detected. + * + * @param e an event containing information about the menu detect + */ +public void menuDetected (MenuDetectEvent e); +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/Packaging.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/Packaging.java new file mode 100644 index 00000000..52f92101 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/Packaging.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + +import org.eclipse.m2e.core.index.SearchExpression; +import org.eclipse.m2e.core.index.SourcedSearchExpression; + + +/** + * Packaging representation. + * + * @author Lukas Krecan + */ +public enum Packaging { + ALL(null), PLUGIN("maven-plugin"), // //$NON-NLS-1$ + POM("pom"); //$NON-NLS-1$ + + private final String text; + + private Packaging(String text) { + this.text = text; + } + + /** + * Text representation of the packaging. + */ + public String getText() { + return text; + } + + public SearchExpression toSearchExpression() { + if(ALL.equals(this)) { + return null; + } + + return new SourcedSearchExpression(getText()); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchEngine.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchEngine.java new file mode 100644 index 00000000..9490a7e7 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchEngine.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + +import java.util.Collection; + + +/** + * Interface to be implemented by a SearchEngine. + * + * @author Lukas Krecan + */ +public interface SearchEngine { + + /** + * Finds groupIds for given expression. + * @param searchExpression + * @param packaging + * @param containingArtifact When looking for exclusion, contains information about artifact we are excluding from. + * @return + */ + public Collection<String> findGroupIds(String searchExpression, Packaging packaging, ArtifactInfo containingArtifact); + + /** + * Finds artifactIds for given expression + * @param groupId + * @param searchExpression + * @param packaging + * @param containingArtifact When looking for exclusion, contains information about artifact we are excluding from. + * @return + */ + public Collection<String> findArtifactIds(String groupId, String searchExpression, Packaging packaging, ArtifactInfo containingArtifact); + + public Collection<String> findVersions(String groupId, String artifactId, String searchExpression, Packaging packaging); + + public Collection<String> findClassifiers(String groupId, String artifactId, String version, String prefix, Packaging packaging); + + public Collection<String> findTypes(String groupId, String artifactId, String version, String prefix, Packaging packaging); + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchException.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchException.java new file mode 100644 index 00000000..606836ed --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/util/search/SearchException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.util.search; + +/** + * @author Lukas Krecan + */ +public class SearchException extends RuntimeException { + + private static final long serialVersionUID = 6909305234190388928L; + + public SearchException(String message, Throwable cause) { + super(message, cause); + } + + public SearchException(String message) { + super(message); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/AbstractMavenWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/AbstractMavenWizardPage.java new file mode 100644 index 00000000..cac68318 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/AbstractMavenWizardPage.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * AbstractMavenImportWizardPage + * + * @author Eugene Kuleshov + */ +public abstract class AbstractMavenWizardPage extends WizardPage { + + /** the history limit */ + protected static final int MAX_HISTORY = 15; + + /** + * The project import configuration + */ + private ProjectImportConfiguration importConfiguration; + + /** The resolver configuration panel */ + protected ResolverConfigurationComponent resolverConfigurationComponent; + + /** dialog settings to store input history */ + protected IDialogSettings dialogSettings; + + /** the Map of field ids to List of comboboxes that share the same history */ + private Map<String, List<Combo>> fieldsWithHistory; + + private boolean isHistoryLoaded = false; + + /** @wbp.parser.constructor */ + protected AbstractMavenWizardPage(String pageName) { + this(pageName, null); + } + + /** + * Creates a page. This constructor should be used for the wizards where you need to have the advanced settings box on + * each page. Pass the same bean to each page so they can share the data. + */ + protected AbstractMavenWizardPage(String pageName, ProjectImportConfiguration importConfiguration) { + super(pageName); + this.importConfiguration = importConfiguration; + + fieldsWithHistory = new HashMap<String, List<Combo>>(); + + initDialogSettings(); + } + + public ProjectImportConfiguration getImportConfiguration() { + return this.importConfiguration; + } + + /** Creates an advanced settings panel. */ + protected void createAdvancedSettings(Composite composite, GridData gridData) { + if(importConfiguration != null) { +// Label separator = new Label(composite, SWT.HORIZONTAL | SWT.SEPARATOR); +// GridData separatorData = new GridData(SWT.FILL, SWT.TOP, false, false, gridData.horizontalSpan, 1); +// separatorData.verticalIndent = 7; +// separator.setLayoutData(separatorData); + gridData.verticalIndent = 7; + + resolverConfigurationComponent = new ResolverConfigurationComponent(composite, importConfiguration, true); + resolverConfigurationComponent.setLayoutData(gridData); + addFieldWithHistory("projectNameTemplate", resolverConfigurationComponent.template); //$NON-NLS-1$ + } + } + + /** Loads the advanced settings data when the page is displayed. */ + public void setVisible(boolean visible) { + if(visible) { + if(!isHistoryLoaded) { + // load data before history kicks in + if(resolverConfigurationComponent != null) { + resolverConfigurationComponent.loadData(); + } + loadInputHistory(); + isHistoryLoaded = true; + } else { + saveInputHistory(); + } + if(resolverConfigurationComponent != null) { + resolverConfigurationComponent.loadData(); + } + } + super.setVisible(visible); + } + + /** Saves the history when the page is disposed. */ + public void dispose() { + saveInputHistory(); + super.dispose(); + } + + /** Loads the dialog settings using the page name as a section name. */ + private void initDialogSettings() { + IDialogSettings pluginSettings; + + // This is strictly to get SWT Designer working locally without blowing up. + if( MavenPlugin.getDefault() == null ) { + pluginSettings = new DialogSettings("Workbench"); + } + else { + pluginSettings = MavenPlugin.getDefault().getDialogSettings(); + } + + dialogSettings = pluginSettings.getSection(getName()); + if(dialogSettings == null) { + dialogSettings = pluginSettings.addNewSection(getName()); + pluginSettings.addSection(dialogSettings); + } + } + + /** Loads the input history from the dialog settings. */ + private void loadInputHistory() { + for(Map.Entry<String, List<Combo>> e : fieldsWithHistory.entrySet()) { + String id = e.getKey(); + String[] items = dialogSettings.getArray(id); + if(items != null) { + for(Combo combo : e.getValue()) { + String text = combo.getText(); + combo.setItems(items); + if(text.length() > 0) { + // setItems() clears the text input, so we need to restore it + combo.setText(text); + } + } + } + } + } + + /** Saves the input history into the dialog settings. */ + private void saveInputHistory() { + for(Map.Entry<String, List<Combo>> e : fieldsWithHistory.entrySet()) { + String id = e.getKey(); + + Set<String> history = new LinkedHashSet<String>(MAX_HISTORY); + + for(Combo combo : e.getValue()) { + String lastValue = combo.getText(); + if(lastValue != null && lastValue.trim().length() > 0) { + history.add(lastValue); + } + } + + Combo combo = e.getValue().iterator().next(); + String[] items = combo.getItems(); + for(int j = 0; j < items.length && history.size() < MAX_HISTORY; j++ ) { + history.add(items[j]); + } + + dialogSettings.put(id, history.toArray(new String[history.size()])); + } + } + + /** Adds an input control to the list of fields to save. */ + protected void addFieldWithHistory(String id, Combo combo) { + if(combo != null) { + List<Combo> combos = fieldsWithHistory.get(id); + if(combos == null) { + combos = new ArrayList<Combo>(); + fieldsWithHistory.put(id, combos); + } + combos.add(combo); + } + } + + protected String validateIdInput(String text, String id) { + if(text == null || text.length() == 0) { + return Messages.getString("wizard.project.page.maven2.validator." + id + "ID"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if(text.contains(" ")) { //$NON-NLS-1$ + return Messages.getString("wizard.project.page.maven2.validator." + id + "IDnospaces"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + IStatus nameStatus = ResourcesPlugin.getWorkspace().validateName(text, IResource.PROJECT); + if(!nameStatus.isOK()) { + return Messages.getString("wizard.project.page.maven2.validator." + id + "IDinvalid", nameStatus.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if(!text.matches("[A-Za-z0-9_\\-.]+")) { //$NON-NLS-1$ + return Messages.getString("wizard.project.page.maven2.validator." + id + "IDinvalid", text); //$NON-NLS-1$ //$NON-NLS-2$ + } + + return null; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/CustomArchetypeDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/CustomArchetypeDialog.java new file mode 100644 index 00000000..bd919d54 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/CustomArchetypeDialog.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.dialogs.DialogSettings; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.Messages; + +/** + * Custom Archetype dialog + * + * @author Eugene Kuleshov + */ +public class CustomArchetypeDialog extends TitleAreaDialog { + + private static final String DIALOG_SETTINGS = CustomArchetypeDialog.class.getName(); + + private static final String KEY_ARCHETYPE_GROUP_ID = "archetypeGroupId"; //$NON-NLS-1$ + + private static final String KEY_ARCHETYPE_ARTIFACT_ID = "archetypeArtifactId"; //$NON-NLS-1$ + + private static final String KEY_ARCHETYPE_VERSION = "archetypeVersion"; //$NON-NLS-1$ + + private static final String KEY_REPOSITORY_URL = "repositoryUrl"; //$NON-NLS-1$ + + private static final int MAX_HISTORY = 15; + + private String title; + + private String message; + + private Combo archetypeGroupIdCombo; + + private Combo archetypeArtifactIdCombo; + + private Combo archetypeVersionCombo; + + private Combo repositoryCombo; + + private IDialogSettings dialogSettings; + + private String archetypeArtifactId; + + private String archetypeGroupId; + + private String archetypeVersion; + + private String repositoryUrl; + + protected CustomArchetypeDialog(Shell shell, String title) { + super(shell); + this.title = title; + this.message = Messages.CustomArchetypeDialog_message; + setShellStyle(SWT.DIALOG_TRIM); + + IDialogSettings pluginSettings = MavenPlugin.getDefault().getDialogSettings(); + dialogSettings = pluginSettings.getSection(DIALOG_SETTINGS); + if(dialogSettings == null) { + dialogSettings = new DialogSettings(DIALOG_SETTINGS); + pluginSettings.addSection(dialogSettings); + } + } + + protected Control createContents(Composite parent) { + Control control = super.createContents(parent); + setTitle(title); + setMessage(message); + return control; + } + + protected Control createDialogArea(Composite parent) { + Composite composite1 = (Composite) super.createDialogArea(parent); + + Composite composite = new Composite(composite1, SWT.NONE); + composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginTop = 7; + gridLayout.marginWidth = 12; + gridLayout.numColumns = 2; + composite.setLayout(gridLayout); + + Label archetypeGroupIdLabel = new Label(composite, SWT.NONE); + archetypeGroupIdLabel.setText(Messages.CustomArchetypeDialog_lblArchetypegroupId); + + archetypeGroupIdCombo = new Combo(composite, SWT.NONE); + GridData archetypeGroupIdComboData = new GridData(SWT.FILL, SWT.CENTER, true, false); + archetypeGroupIdComboData.widthHint = 350; + archetypeGroupIdCombo.setLayoutData(archetypeGroupIdComboData); + archetypeGroupIdCombo.setItems(getSavedValues(KEY_ARCHETYPE_GROUP_ID)); + archetypeGroupIdCombo.setData("name", "archetypeGroupId"); //$NON-NLS-1$ //$NON-NLS-2$ + + Label archetypeArtifactIdLabel = new Label(composite, SWT.NONE); + archetypeArtifactIdLabel.setText(Messages.CustomArchetypeDialog_lblArchetypeartifactid); + + archetypeArtifactIdCombo = new Combo(composite, SWT.NONE); + archetypeArtifactIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + archetypeArtifactIdCombo.setItems(getSavedValues(KEY_ARCHETYPE_ARTIFACT_ID)); + archetypeArtifactIdCombo.setData("name", "archetypeArtifactId"); //$NON-NLS-1$ //$NON-NLS-2$ + + Label archetypeVersionLabel = new Label(composite, SWT.NONE); + archetypeVersionLabel.setText(Messages.CustomArchetypeDialog_lblArchetypeversion); + + archetypeVersionCombo = new Combo(composite, SWT.NONE); + archetypeVersionCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + archetypeVersionCombo.setItems(getSavedValues(KEY_ARCHETYPE_VERSION)); + archetypeVersionCombo.setData("name", "archetypeVersion"); //$NON-NLS-1$ //$NON-NLS-2$ + + Label repositoryLabel = new Label(composite, SWT.NONE); + repositoryLabel.setText(Messages.CustomArchetypeDialog_lblRepo); + + repositoryCombo = new Combo(composite, SWT.NONE); + repositoryCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + repositoryCombo.setItems(getSavedValues(KEY_REPOSITORY_URL)); + repositoryCombo.setData("name", "repository"); //$NON-NLS-1$ //$NON-NLS-2$ + + ModifyListener modifyListener = new ModifyListener() { + public void modifyText(final ModifyEvent e) { + update(); + } + }; + + archetypeGroupIdCombo.addModifyListener(modifyListener); + archetypeArtifactIdCombo.addModifyListener(modifyListener); + archetypeVersionCombo.addModifyListener(modifyListener); + repositoryCombo.addModifyListener(modifyListener); + +// fullIndexButton = new Button(composite, SWT.CHECK); +// fullIndexButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); +// fullIndexButton.setText("&Full Index"); +// fullIndexButton.setSelection(true); + + return composite; + } + + private String[] getSavedValues(String key) { + String[] array = dialogSettings.getArray(key); + return array == null ? new String[0] : array; + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + public void create() { + super.create(); + getButton(IDialogConstants.OK_ID).setEnabled(false); + } + + protected void okPressed() { + archetypeArtifactId = archetypeArtifactIdCombo.getText().trim(); + archetypeGroupId = archetypeGroupIdCombo.getText().trim(); + archetypeVersion = archetypeVersionCombo.getText().trim(); + repositoryUrl = repositoryCombo.getText().trim(); + + saveValue(KEY_ARCHETYPE_GROUP_ID, archetypeGroupId); + saveValue(KEY_ARCHETYPE_ARTIFACT_ID, archetypeArtifactId); + saveValue(KEY_ARCHETYPE_VERSION, archetypeVersion); + saveValue(KEY_REPOSITORY_URL, repositoryUrl); + + super.okPressed(); + } + + public String getArchetypeGroupId() { + return archetypeGroupId; + } + + public String getArchetypeArtifactId() { + return archetypeArtifactId; + } + + public String getArchetypeVersion() { + return archetypeVersion; + } + + public String getRepositoryUrl() { + return repositoryUrl; + } + + private void saveValue(String key, String value) { + List<String> dirs = new ArrayList<String>(); + dirs.addAll(Arrays.asList(getSavedValues(key))); + + dirs.remove(value); + dirs.add(0, value); + + if(dirs.size() > MAX_HISTORY) { + dirs = dirs.subList(0, MAX_HISTORY); + } + + dialogSettings.put(key, dirs.toArray(new String[dirs.size()])); + } + + void update() { + boolean isValid = isValid(); + // verifyButton.setEnabled(isValid); + getButton(IDialogConstants.OK_ID).setEnabled(isValid); + } + + private boolean isValid() { + setErrorMessage(null); + setMessage(null, IStatus.WARNING); + + if(archetypeGroupIdCombo.getText().trim().length()==0) { + setErrorMessage(Messages.CustomArchetypeDialog_error_grid); + return false; + } + + if(archetypeArtifactIdCombo.getText().trim().length()==0) { + setErrorMessage(Messages.CustomArchetypeDialog_error_artid); + return false; + } + + if(archetypeVersionCombo.getText().trim().length()==0) { + setErrorMessage(Messages.CustomArchetypeDialog_error_version); + return false; + } + + // TODO check if archetype available locally + + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenArtifactComponent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenArtifactComponent.java new file mode 100644 index 00000000..9935ca36 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenArtifactComponent.java @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.core.Messages; + + +public class MavenArtifactComponent extends Composite { + + public static final String JAR = "jar"; //$NON-NLS-1$ + + public static final String WAR = "war"; //$NON-NLS-1$ + + public static final String EAR = "ear"; //$NON-NLS-1$ + + public static final String RAR = "rar"; //$NON-NLS-1$ + + public static final String POM = "pom"; //$NON-NLS-1$ + + // MNGECLIPSE-688 add EJB Support + public static final String EJB = "ejb"; //$NON-NLS-1$ + + public static final String[] PACKAGING_OPTIONS = {JAR, WAR, EJB, EAR, RAR, POM, // + "maven-plugin", "maven-archetype", "osgi-bundle", "eclipse-plugin"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + public static final String DEFAULT_PACKAGING = JAR; + + public static final String DEFAULT_VERSION = "0.0.1-SNAPSHOT"; //$NON-NLS-1$ + + /** group id text field */ + protected Combo groupIdCombo; + + /** artifact id text field */ + protected Combo artifactIdCombo; + + /** version text field */ + protected Combo versionCombo; + + /** packaging options combobox */ + protected Combo packagingCombo; + + /** name text field */ + protected Combo nameCombo; + + /** description text field */ + protected Text descriptionText; + + private ModifyListener modifyingListener; + + private Label groupIdlabel; + + private Label artifactIdLabel; + + private Label versionLabel; + + private Label packagingLabel; + + private Label nameLabel; + + private Label descriptionLabel; + + /** Creates a new component. */ + public MavenArtifactComponent(Composite parent, int styles) { + super(parent, styles); + + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.numColumns = 2; + setLayout(layout); + + Group artifactGroup = new Group(this, SWT.NONE); + artifactGroup.setText(Messages.getString("artifactComponent.artifact")); //$NON-NLS-1$ + GridData gd_artifactGroup = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1); + artifactGroup.setLayoutData(gd_artifactGroup); + artifactGroup.setLayout(new GridLayout(2, false)); + + groupIdlabel = new Label(artifactGroup, SWT.NONE); + groupIdlabel.setText(Messages.getString("artifactComponent.groupId")); //$NON-NLS-1$ + + groupIdCombo = new Combo(artifactGroup, SWT.BORDER); + groupIdCombo.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); + groupIdCombo.setData("name", "groupIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + artifactIdLabel = new Label(artifactGroup, SWT.NONE); + artifactIdLabel.setText(Messages.getString("artifactComponent.artifactId")); //$NON-NLS-1$ + + artifactIdCombo = new Combo(artifactGroup, SWT.BORDER); + artifactIdCombo.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false)); + artifactIdCombo.setData("name", "artifactIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + versionLabel = new Label(artifactGroup, SWT.NONE); + versionLabel.setText(Messages.getString("artifactComponent.version")); //$NON-NLS-1$ + + versionCombo = new Combo(artifactGroup, SWT.BORDER); + versionCombo.setLayoutData(new GridData(150, SWT.DEFAULT)); + versionCombo.setText(DEFAULT_VERSION); + versionCombo.setData("name", "versionCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + packagingLabel = new Label(artifactGroup, SWT.NONE); + packagingLabel.setText(Messages.getString("artifactComponent.packaging")); //$NON-NLS-1$ + + packagingCombo = new Combo(artifactGroup, SWT.NONE); + packagingCombo.setItems(PACKAGING_OPTIONS); + packagingCombo.setText(DEFAULT_PACKAGING); + packagingCombo.setLayoutData(new GridData(150, SWT.DEFAULT)); + packagingCombo.setData("name", "packagingCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + nameLabel = new Label(artifactGroup, SWT.NONE); + nameLabel.setLayoutData(new GridData()); + nameLabel.setText(Messages.getString("artifactComponent.name")); //$NON-NLS-1$ + + nameCombo = new Combo(artifactGroup, SWT.BORDER); + nameCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + nameCombo.setData("name", "nameCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + descriptionLabel = new Label(artifactGroup, SWT.NONE); + descriptionLabel.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false)); + descriptionLabel.setText(Messages.getString("artifactComponent.description")); //$NON-NLS-1$ + + descriptionText = new Text(artifactGroup, SWT.V_SCROLL | SWT.BORDER | SWT.WRAP); + GridData gd_descriptionText = new GridData(SWT.FILL, SWT.FILL, false, true); + gd_descriptionText.minimumHeight = 20; + descriptionText.setLayoutData(gd_descriptionText); + descriptionText.setData("name", "descriptionText"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void setModifyingListener(ModifyListener modifyingListener) { + this.modifyingListener = modifyingListener; + + groupIdCombo.addModifyListener(modifyingListener); + artifactIdCombo.addModifyListener(modifyingListener); + versionCombo.addModifyListener(modifyingListener); + packagingCombo.addModifyListener(modifyingListener); + } + + public void dispose() { + super.dispose(); + + if(modifyingListener != null) { + groupIdCombo.removeModifyListener(modifyingListener); + artifactIdCombo.removeModifyListener(modifyingListener); + versionCombo.removeModifyListener(modifyingListener); + packagingCombo.removeModifyListener(modifyingListener); + } + } + + public String getModelName() { + return nameCombo.getText(); + } + + public String getArtifactId() { + return this.artifactIdCombo.getText(); + } + + public String getGroupId() { + return this.groupIdCombo.getText(); + } + + public String getVersion() { + return this.versionCombo.getText(); + } + + public String getPackaging() { + return packagingCombo.getText(); + } + + public String getDescription() { + return descriptionText.getText(); + } + + public void setModelName(String name) { + nameCombo.setText(name); + } + + public void setGroupId(String groupId) { + this.groupIdCombo.setText(groupId); + } + + public void setArtifactId(String artifact) { + this.artifactIdCombo.setText(artifact); + } + + public void setVersion(String version) { + versionCombo.setText(version); + } + + public void setPackaging(String packaging) { + if(packagingCombo != null) { + packagingCombo.setText(packaging); + } + } + + public void setDescription(String description) { + if(descriptionText != null) { + descriptionText.setText(description); + } + } + + public Model getModel() { + Model model = new Model(); + model.setModelVersion("4.0.0"); //$NON-NLS-1$ + + model.setGroupId(getGroupId()); + model.setArtifactId(getArtifactId()); + model.setVersion(getVersion()); + model.setPackaging(getPackaging()); + + if(getModelName().length()>0) { + model.setName(getModelName()); + } + if(getDescription().length()>0) { + model.setDescription(getDescription()); + } + + return model; + } + + /** Enables or disables the artifact id text field. */ + public void setArtifactIdEditable(boolean b) { + artifactIdCombo.setEnabled(b); + } + + public Combo getGroupIdCombo() { + return groupIdCombo; + } + + public Combo getArtifactIdCombo() { + return artifactIdCombo; + } + + public Combo getVersionCombo() { + return versionCombo; + } + + public Combo getNameCombo() { + return nameCombo; + } + + public void setWidthGroup(WidthGroup widthGroup) { + widthGroup.addControl(this.groupIdlabel); + widthGroup.addControl(this.artifactIdLabel); + widthGroup.addControl(this.versionLabel); + widthGroup.addControl(this.packagingLabel); + widthGroup.addControl(this.nameLabel); + widthGroup.addControl(this.descriptionLabel); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutLocationPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutLocationPage.java new file mode 100644 index 00000000..e840467e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutLocationPage.java @@ -0,0 +1,424 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.model.Scm; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.scm.ScmHandlerFactory; +import org.eclipse.m2e.core.scm.ScmHandlerUi; +import org.eclipse.m2e.core.scm.ScmTag; +import org.eclipse.m2e.core.scm.ScmUrl; + + +/** + * @author Eugene Kuleshov + */ +public class MavenCheckoutLocationPage extends AbstractMavenWizardPage { + + String scmType; + ScmUrl[] scmUrls; + String scmParentUrl; + + Combo scmTypeCombo; + + Combo scmUrlCombo; + + Button scmUrlBrowseButton; + + Button headRevisionButton; + + Label revisionLabel; + + Text revisionText; + + Button revisionBrowseButton; + + private Button checkoutAllProjectsButton; + + protected MavenCheckoutLocationPage(ProjectImportConfiguration projectImportConfiguration) { + super("MavenCheckoutLocationPage", projectImportConfiguration); + setTitle(Messages.MavenCheckoutLocationPage_title); + setDescription(Messages.MavenCheckoutLocationPage_description); + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(5, false); + gridLayout.verticalSpacing = 0; + composite.setLayout(gridLayout); + setControl(composite); + + SelectionAdapter selectionAdapter = new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + updatePage(); + } + }; + + if(scmUrls == null || scmUrls.length < 2) { + Label urlLabel = new Label(composite, SWT.NONE); + urlLabel.setLayoutData(new GridData()); + urlLabel.setText(Messages.MavenCheckoutLocationPage_lblurl); + + scmTypeCombo = new Combo(composite, SWT.READ_ONLY); + scmTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + scmTypeCombo.setData("name", "mavenCheckoutLocation.typeCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + String[] types = ScmHandlerFactory.getTypes(); + for(int i = 0; i < types.length; i++ ) { + scmTypeCombo.add(types[i]); + } + scmTypeCombo.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + String newScmType = scmTypeCombo.getText(); + if(!newScmType.equals(scmType)) { + scmType = newScmType; + scmUrlCombo.setText(""); //$NON-NLS-1$ + updatePage(); + } + } + }); + + if(scmUrls!=null && scmUrls.length == 1) { + try { + scmType = ScmUrl.getType(scmUrls[0].getUrl()); + } catch(CoreException ex) { + } + } + + scmUrlCombo = new Combo(composite, SWT.NONE); + scmUrlCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + scmUrlCombo.setData("name", "mavenCheckoutLocation.urlCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + scmUrlBrowseButton = new Button(composite, SWT.NONE); + scmUrlBrowseButton.setLayoutData(new GridData()); + scmUrlBrowseButton.setText(Messages.MavenCheckoutLocationPage_btnBrowse); + } + + headRevisionButton = new Button(composite, SWT.CHECK); + GridData headRevisionButtonData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 5, 1); + headRevisionButtonData.verticalIndent = 5; + headRevisionButton.setLayoutData(headRevisionButtonData); + headRevisionButton.setText(Messages.MavenCheckoutLocationPage_btnHead); + headRevisionButton.setSelection(true); + headRevisionButton.addSelectionListener(selectionAdapter); + + revisionLabel = new Label(composite, SWT.RADIO); + GridData revisionButtonData = new GridData(); + revisionButtonData.horizontalIndent = 10; + revisionLabel.setLayoutData(revisionButtonData); + revisionLabel.setText(Messages.MavenCheckoutLocationPage_lblRevision); + // revisionButton.addSelectionListener(selectionAdapter); + + revisionText = new Text(composite, SWT.BORDER); + GridData revisionTextData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + revisionTextData.widthHint = 115; + revisionTextData.verticalIndent = 3; + revisionText.setLayoutData(revisionTextData); + + if(scmUrls != null) { + ScmTag tag = scmUrls[0].getTag(); + if(tag!=null) { + headRevisionButton.setSelection(false); + revisionText.setText(tag.getName()); + } + } + + revisionText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updatePage(); + } + }); + + revisionBrowseButton = new Button(composite, SWT.NONE); + GridData gd_revisionBrowseButton = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + gd_revisionBrowseButton.verticalIndent = 3; + revisionBrowseButton.setLayoutData(gd_revisionBrowseButton); + revisionBrowseButton.setText(Messages.MavenCheckoutLocationPage_btnRevSelect); + revisionBrowseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + String url = scmParentUrl; + if(url==null) { + return; + } + + String scmType = scmTypeCombo.getText(); + + ScmHandlerUi handlerUi = ScmHandlerFactory.getHandlerUiByType(scmType); + String revision = handlerUi.selectRevision(getShell(), scmUrls[0], revisionText.getText()); + if(revision!=null) { + revisionText.setText(revision); + headRevisionButton.setSelection(false); + updatePage(); + } + } + }); + + checkoutAllProjectsButton = new Button(composite, SWT.CHECK); + GridData checkoutAllProjectsData = new GridData(SWT.LEFT, SWT.TOP, true, false, 5, 1); + checkoutAllProjectsData.verticalIndent = 10; + checkoutAllProjectsButton.setLayoutData(checkoutAllProjectsData); + checkoutAllProjectsButton.setText(Messages.MavenCheckoutLocationPage_btnCheckout); + checkoutAllProjectsButton.setSelection(true); + checkoutAllProjectsButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + updatePage(); + } + }); + + GridData advancedSettingsData = new GridData(SWT.FILL, SWT.TOP, true, false, 5, 1); + advancedSettingsData.verticalIndent = 10; + createAdvancedSettings(composite, advancedSettingsData); + + if(scmUrls!=null && scmUrls.length == 1) { + scmTypeCombo.setText(scmType == null ? "" : scmType); //$NON-NLS-1$ + scmUrlCombo.setText(scmUrls[0].getProviderUrl()); + } + + if(scmUrls == null || scmUrls.length < 2) { + scmUrlBrowseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + ScmHandlerUi handlerUi = ScmHandlerFactory.getHandlerUiByType(scmType); + // XXX should use null if there is no scmUrl selected + ScmUrl currentUrl = scmUrls==null || scmUrls.length==0 ? new ScmUrl("scm:" + scmType + ":") : scmUrls[0]; //$NON-NLS-1$ //$NON-NLS-2$ + ScmUrl scmUrl = handlerUi.selectUrl(getShell(), currentUrl); + if(scmUrl!=null) { + scmUrlCombo.setText(scmUrl.getProviderUrl()); + if(scmUrls==null) { + scmUrls = new ScmUrl[1]; + } + scmUrls[0] = scmUrl; + scmParentUrl = scmUrl.getUrl(); + updatePage(); + } + } + }); + + scmUrlCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + final String url = scmUrlCombo.getText(); + if(url.startsWith("scm:")) { //$NON-NLS-1$ + try { + final String type = ScmUrl.getType(url); + scmTypeCombo.setText(type); + scmType = type; + Display.getDefault().asyncExec(new Runnable() { + public void run() { + scmUrlCombo.setText(url.substring(type.length() + 5)); + } + }); + } catch(CoreException ex) { + } + return; + } + + if(scmUrls==null) { + scmUrls = new ScmUrl[1]; + } + + ScmUrl scmUrl = new ScmUrl("scm:" + scmType + ":" + url); //$NON-NLS-1$ //$NON-NLS-2$ + scmUrls[0] = scmUrl; + scmParentUrl = scmUrl.getUrl(); + updatePage(); + } + }); + } + + updatePage(); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.wizards.AbstractMavenWizardPage#setVisible(boolean) + */ + public void setVisible(boolean visible) { + super.setVisible(visible); + + if(dialogSettings!=null && scmUrlCombo!=null) { + String[] items = dialogSettings.getArray("scmUrl"); //$NON-NLS-1$ + if(items != null) { + String text = scmUrlCombo.getText(); + scmUrlCombo.setItems(items); + if (text.length() > 0) { + // setItems() clears the text input, so we need to restore it + scmUrlCombo.setText(text); + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.wizards.AbstractMavenWizardPage#dispose() + */ + public void dispose() { + if(dialogSettings != null && scmUrlCombo!=null) { + Set<String> history = new LinkedHashSet<String>(MAX_HISTORY); + + String lastValue = scmUrlCombo.getText(); + if ( lastValue!=null && lastValue.trim().length() > 0 ) { + history.add("scm:" + scmType + ":" + lastValue); //$NON-NLS-1$ //$NON-NLS-2$ + } + + String[] items = scmUrlCombo.getItems(); + for(int j = 0; j < items.length && history.size() < MAX_HISTORY; j++ ) { + history.add(items[j]); + } + + dialogSettings.put("scmUrl", history.toArray(new String[history.size()])); //$NON-NLS-1$ + } + + super.dispose(); + } + + public IWizardContainer getContainer() { + return super.getContainer(); + } + + void updatePage() { + boolean canSelectUrl = false ; + boolean canSelectRevision = false; + ScmHandlerUi handlerUi = ScmHandlerFactory.getHandlerUiByType(scmType); + if(handlerUi!=null) { + canSelectUrl = handlerUi.canSelectUrl(); + canSelectRevision = handlerUi.canSelectRevision(); + } + + if(scmUrlBrowseButton!=null) { + scmUrlBrowseButton.setEnabled(canSelectUrl); + } + + revisionBrowseButton.setEnabled(canSelectRevision); + + boolean isHeadRevision = isHeadRevision(); + revisionLabel.setEnabled(!isHeadRevision); + revisionText.setEnabled(!isHeadRevision); + + setPageComplete(isPageValid()); + } + + private boolean isPageValid() { + setErrorMessage(null); + + if(scmUrls != null && scmUrls.length < 2) { + if(scmType == null) { + setErrorMessage(Messages.MavenCheckoutLocationPage_error_empty); + return false; + } + } + + ScmHandlerUi handlerUi = ScmHandlerFactory.getHandlerUiByType(scmType); + + if(scmUrls == null || scmUrls.length < 2) { + if(scmUrls == null || scmUrls.length == 0) { + setErrorMessage(Messages.MavenCheckoutLocationPage_error_empty_url); + return false; + } + + if(handlerUi!=null && !handlerUi.isValidUrl(scmUrls[0].getUrl())) { + setErrorMessage(Messages.MavenCheckoutLocationPage_error_url_empty); + return false; + } + } + + if(!isHeadRevision()) { + String revision = revisionText.getText().trim(); + if(revision.length()==0) { + setErrorMessage(Messages.MavenCheckoutLocationPage_error_scm_empty); + return false; + } + + if(handlerUi!=null && !handlerUi.isValidRevision(null, revision)) { + setErrorMessage(Messages.MavenCheckoutLocationPage_error_scm_invalid); + return false; + } + } + + return true; + } + + public void setParent(String parentUrl) { + this.scmParentUrl = parentUrl; + } + + public void setUrls(ScmUrl[] urls) { + this.scmUrls = urls; + } + + public ScmUrl[] getUrls() { + return scmUrls; + } + + public Scm[] getScms() { + if(scmUrls==null) { + return new Scm[0]; + } + + String revision = getRevision(); + Scm[] scms = new Scm[scmUrls.length]; + for(int i = 0; i < scms.length; i++ ) { + Scm scm = new Scm(); + scm.setConnection(scmUrls[i].getUrl()); + scm.setTag(revision); + scms[i] = scm; + } + return scms; + } + + public boolean isCheckoutAllProjects() { + return checkoutAllProjectsButton.getSelection(); + } + + public boolean isHeadRevision() { + return headRevisionButton.getSelection(); + } + + public String getRevision() { + if(isHeadRevision()) { + return "HEAD"; //$NON-NLS-1$ + } + return revisionText.getText().trim(); + } + + public void addListener(final SelectionListener listener) { + ModifyListener listenerProxy = new ModifyListener() { + public void modifyText(ModifyEvent e) { + Event event = new Event(); + event.widget = e.widget; + listener.widgetSelected(new SelectionEvent(event)); + } + }; + scmUrlCombo.addModifyListener(listenerProxy); + revisionText.addModifyListener(listenerProxy); + headRevisionButton.addSelectionListener(listener); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutWizard.java new file mode 100644 index 00000000..53f3b54a --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenCheckoutWizard.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.core.runtime.IAdapterManager; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; + +import org.apache.maven.model.Scm; + +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.MavenProjectScmInfo; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.scm.ScmUrl; + + +/** + * Maven checkout wizard + * + * @author Eugene Kuleshov + */ +public class MavenCheckoutWizard extends Wizard implements IImportWizard, INewWizard { + + private final ProjectImportConfiguration importConfiguration = new ProjectImportConfiguration(); + + private ScmUrl[] urls; + + private String parentUrl; + + private MavenCheckoutLocationPage scheckoutPage; + + private MavenProjectWizardLocationPage locationPage; + + private IStructuredSelection selection; + + + public MavenCheckoutWizard() { + this(null); + setNeedsProgressMonitor(true); + } + + public MavenCheckoutWizard(ScmUrl[] urls) { + setUrls(urls); + setNeedsProgressMonitor(true); + setWindowTitle(Messages.MavenCheckoutWizard_title); + } + + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + + importConfiguration.setWorkingSet(SelectionUtil.getSelectedWorkingSet(selection)); + + ArrayList<ScmUrl> urls = new ArrayList<ScmUrl>(); + IAdapterManager adapterManager = Platform.getAdapterManager(); + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + ScmUrl url = (ScmUrl) adapterManager.getAdapter(it.next(), ScmUrl.class); + if(url!=null) { + urls.add(url); + } + } + setUrls(urls.toArray(new ScmUrl[urls.size()])); + } + + private void setUrls(ScmUrl[] urls) { + if(urls!=null && urls.length>0) { + this.urls = urls; + this.parentUrl = getParentUrl(urls); + } + } + + private String getParentUrl(ScmUrl[] urls) { + if(urls.length==1) { + return urls[0].getUrl(); + } + + String parent = urls[0].getParentUrl(); + for(int i = 1; parent!=null && i < urls.length; i++ ) { + String url = urls[i].getParentUrl(); + if(!parent.equals(url)) { + parent = null; + } + } + return parent; + } + + public void addPages() { + scheckoutPage = new MavenCheckoutLocationPage(importConfiguration); + scheckoutPage.setUrls(urls); + scheckoutPage.setParent(parentUrl); + + locationPage = new MavenProjectWizardLocationPage(importConfiguration, // + Messages.MavenCheckoutWizard_location1, + Messages.MavenCheckoutWizard_location2); + locationPage.setLocationPath(SelectionUtil.getSelectedLocation(selection)); + + addPage(scheckoutPage); + addPage(locationPage); + } + +// /** Adds the listeners after the page controls are created. */ +// public void createPageControls(Composite pageContainer) { +// super.createPageControls(pageContainer); +// +// locationPage.addListener(new SelectionAdapter() { +// public void widgetSelected(SelectionEvent e) { +// projectsPage.setScms(locationPage.getScms(new NullProgressMonitor())); +// } +// }); +// +// projectsPage.setScms(locationPage.getScms(new NullProgressMonitor())); +// } + + public boolean canFinish() { + if(scheckoutPage.isCheckoutAllProjects() && scheckoutPage.isPageComplete()) { + return true; + } + return super.canFinish(); + } + + public boolean performFinish() { + if(!canFinish()) { + return false; + } + + final boolean checkoutAllProjects = scheckoutPage.isCheckoutAllProjects(); + + Scm[] scms = scheckoutPage.getScms(); + + final Collection<MavenProjectScmInfo> mavenProjects = new ArrayList<MavenProjectScmInfo>(); + for(int i = 0; i < scms.length; i++ ) { + String url = scms[i].getConnection(); + String revision = scms[i].getTag(); + + if(url.endsWith("/")) { //$NON-NLS-1$ + url = url.substring(0, url.length()-1); + } + + int n = url.lastIndexOf("/"); //$NON-NLS-1$ + String label = (n == -1 ? url : url.substring(n)) + "/" + IMavenConstants.POM_FILE_NAME; //$NON-NLS-1$ + MavenProjectScmInfo projectInfo = new MavenProjectScmInfo(label, null, // + null, revision, url, url); + mavenProjects.add(projectInfo); + } + + MavenProjectCheckoutJob job = new MavenProjectCheckoutJob(importConfiguration, checkoutAllProjects) { + protected Collection<MavenProjectScmInfo> getProjects(IProgressMonitor monitor) { + return mavenProjects; + } + }; + + if(!locationPage.isInWorkspace()) { + job.setLocation(locationPage.getLocationPath().toFile()); + } + + job.schedule(); + + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenDependenciesWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenDependenciesWizardPage.java new file mode 100644 index 00000000..4e66a4d4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenDependenciesWizardPage.java @@ -0,0 +1,291 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import org.apache.maven.model.Dependency; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; + + +/** + * Wizard page for gathering information about Maven artifacts. Allows to select + * artifacts from the repository index. + */ +public class MavenDependenciesWizardPage extends AbstractMavenWizardPage { + + /** + * Viewer containing dependencies + */ + TableViewer dependencyViewer; + + private Dependency[] dependencies; + + /** + * Listeners notified about all changes + */ + private List<ISelectionChangedListener> listeners = new ArrayList<ISelectionChangedListener>(); + + boolean showScope = false; + + public MavenDependenciesWizardPage() { + this(null, Messages.getString("wizard.project.page.dependencies.title"), // //$NON-NLS-1$ + Messages.getString("wizard.project.page.dependencies.description")); //$NON-NLS-1$ + } + + public MavenDependenciesWizardPage(ProjectImportConfiguration projectImportConfiguration, String title, String description) { + super("MavenDependenciesWizardPage", projectImportConfiguration); //$NON-NLS-1$ + setTitle(title); + setDescription(description); + setPageComplete(true); + } + + public void setShowScope(boolean showScope) { + this.showScope = showScope; + } + + public void setDependencies(Dependency[] dependencies) { + this.dependencies = dependencies; + } + + /** + * {@inheritDoc} This wizard page contains a <code>TableViewer</code> to display the currently included Maven2 + * directories and a button area with buttons to add further dependencies or remove existing ones. + */ + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + GridLayout layout = new GridLayout(3, false); + composite.setLayout(layout); + + if(dependencies!=null) { + createArtifacts(composite); + } + + createAdvancedSettings(composite, new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + + setControl(composite); + + updatePage(); + } + + private void createArtifacts(Composite composite) { + Label mavenArtifactsLabel = new Label(composite, SWT.NONE); + mavenArtifactsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); + mavenArtifactsLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenDependenciesWizardPage_lblArtifacts); + + dependencyViewer = new TableViewer(composite, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); + dependencyViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2)); + dependencyViewer.setUseHashlookup(true); + dependencyViewer.setLabelProvider(new ArtifactLabelProvider()); + dependencyViewer.setComparator(new DependencySorter()); + dependencyViewer.add(dependencies); + + Button addDependencyButton = new Button(composite, SWT.PUSH); + GridData gd_addDependencyButton = new GridData(SWT.FILL, SWT.TOP, false, false); + addDependencyButton.setLayoutData(gd_addDependencyButton); + addDependencyButton.setText(Messages.getString("wizard.project.page.dependencies.add")); //$NON-NLS-1$ + + addDependencyButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(getShell(), // + org.eclipse.m2e.core.internal.Messages.MavenDependenciesWizardPage_searchDialog_title, IIndex.SEARCH_ARTIFACT, Collections.<ArtifactKey>emptySet(), showScope); + if(dialog.open() == Window.OK) { + Object result = dialog.getFirstResult(); + if(result instanceof IndexedArtifactFile) { + Dependency dependency = ((IndexedArtifactFile) result).getDependency(); + dependency.setScope(dialog.getSelectedScope()); + dependencyViewer.add(dependency); + notifyListeners(); + } else if(result instanceof IndexedArtifact) { + // If we have an ArtifactInfo, we add the first FileInfo it contains + // which corresponds to the latest version of the artifact. + Set<IndexedArtifactFile> files = ((IndexedArtifact) result).getFiles(); + if(files != null && !files.isEmpty()) { + dependencyViewer.add(files.iterator().next().getDependency()); + notifyListeners(); + } + } + } + } + }); + + final Button removeDependencyButton = new Button(composite, SWT.PUSH); + removeDependencyButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true)); + removeDependencyButton.setText(Messages.getString("wizard.project.page.dependencies.remove")); //$NON-NLS-1$ + removeDependencyButton.setEnabled(false); + + removeDependencyButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + IStructuredSelection selection = (IStructuredSelection) dependencyViewer.getSelection(); + if(selection != null) { + dependencyViewer.remove(selection.toArray()); + notifyListeners(); + } + } + }); + + dependencyViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + removeDependencyButton.setEnabled(selection.size() > 0); + } + }); + } + + public IWizardContainer getContainer() { + return super.getContainer(); + } + + void updatePage() { + setPageComplete(isPageValid()); + } + + private boolean isPageValid() { + setErrorMessage(null); + return true; + } + + /** + * Notify listeners about changes + */ + protected void notifyListeners() { + SelectionChangedEvent event = new SelectionChangedEvent(dependencyViewer, dependencyViewer.getSelection()); + for(ISelectionChangedListener listener : listeners) { + listener.selectionChanged(event); + } + } + + public void addListener(ISelectionChangedListener listener) { + listeners.add(listener); + } + + /** + * Returns dependencies currently chosen by the user. + * + * @return dependencies currently chosen by the user. Neither the array nor any of its elements is + * <code>null</code>. + */ + public Dependency[] getDependencies() { + List<Dependency> dependencies = new ArrayList<Dependency>(); + for(int i = 0; i < dependencyViewer.getTable().getItemCount(); i++ ) { + Object element = dependencyViewer.getElementAt(i); + if(element instanceof Dependency) { + dependencies.add((Dependency) element); + } + } + return dependencies.toArray(new Dependency[dependencies.size()]); + } + + + /** + * Simple <code>LabelProvider</code> attached to the dependency viewer. + * <p> + * The information displayed for objects of type <code>Dependency</code> inside the dependency viewer is the + * following: + * </p> + * <p> + * {groupId} - {artifactId} - {version} - {type} + * </p> + */ + public static class ArtifactLabelProvider extends LabelProvider { + + /** The image to show for all objects of type <code>Dependency</code>. */ + private static final Image DEPENDENCY_IMAGE = MavenImages.IMG_JAR; + + /** + * {@inheritDoc} + * <p> + * The text returned for objects of type <code>Dependency</code> contains the following information about the + * dependency: + * </p> + * <p> + * {groupId} - {artifactId} - {version} - {type} + * </p> + */ + public String getText(Object element) { + if(element instanceof Dependency) { + Dependency d = (Dependency) element; + return d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion() + (d.getClassifier() == null ? "" : ":" + d.getClassifier()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + return super.getText(element); + } + + public Image getImage(Object element) { + if(element instanceof Dependency) { + return DEPENDENCY_IMAGE; + } + return super.getImage(element); + } + } + + /** + * Simple <code>ViewerComparator</code> attached to the dependency viewer. Objects of type <code>Dependency</code> are + * sorted by (1) their groupId and (2) their artifactId. + */ + public static class DependencySorter extends ViewerComparator { + + /** + * Two objects of type <code>Dependency</code> are sorted by (1) their groupId and (2) their artifactId. + */ + public int compare(Viewer viewer, Object e1, Object e2) { + if(!(e1 instanceof Dependency) || !(e2 instanceof Dependency)) { + return super.compare(viewer, e1, e2); + } + + // First of all, compare the group IDs of the two dependencies. + String group1 = ((Dependency) e1).getGroupId(); + String group2 = ((Dependency) e2).getGroupId(); + + int result = (group1 == null) ? -1 : group1.compareToIgnoreCase(group2); + + // If the group IDs match, we sort by the artifact IDs. + if(result == 0) { + String artifact1 = ((Dependency) e1).getArtifactId(); + String artifact2 = ((Dependency) e2).getArtifactId(); + result = artifact1 == null ? -1 : artifact1.compareToIgnoreCase(artifact2); + } + + return result; + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizard.java new file mode 100644 index 00000000..3eeafc8b --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizard.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.progress.IProgressConstants; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.MavenProjectInfo; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Maven Import Wizard + * + * @author Eugene Kuleshov + */ +public class MavenImportWizard extends Wizard implements IImportWizard { + + final ProjectImportConfiguration importConfiguration; + + private MavenImportWizardPage page; + + private List<String> locations; + + private boolean showLocation = true; + + public MavenImportWizard() { + importConfiguration = new ProjectImportConfiguration(); + setNeedsProgressMonitor(true); + setWindowTitle(Messages.MavenImportWizard_title); + } + + public MavenImportWizard(ProjectImportConfiguration importConfiguration, List<String> locations) { + this.importConfiguration = importConfiguration; + this.locations = locations; + this.showLocation = false; + setNeedsProgressMonitor(true); + } + + public void init(IWorkbench workbench, IStructuredSelection selection) { + if(locations == null || locations.isEmpty()) { + IPath location = SelectionUtil.getSelectedLocation(selection); + if(location != null) { + locations = Collections.singletonList(location.toOSString()); + } + } + + importConfiguration.setWorkingSet(SelectionUtil.getSelectedWorkingSet(selection)); + } + + public void addPages() { + page = new MavenImportWizardPage(importConfiguration); + page.setLocations(locations); + page.setShowLocation(showLocation); + addPage(page); + } + + public boolean performFinish() { + if(!page.isPageComplete()) { + return false; + } + + final Collection<MavenProjectInfo> projects = page.getProjects(); + + final MavenPlugin plugin = MavenPlugin.getDefault(); + + Job job = new WorkspaceJob(Messages.MavenImportWizard_job) { + public IStatus runInWorkspace(IProgressMonitor monitor) { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + try { + plugin.getProjectConfigurationManager().importProjects(projects, importConfiguration, monitor); + } catch(CoreException ex) { + plugin.getConsole().logError("Projects imported with errors"); + return ex.getStatus(); + } + + return Status.OK_STATUS; + } + }; + job.setRule(plugin.getProjectConfigurationManager().getRule()); + job.schedule(); + + return true; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizardPage.java new file mode 100644 index 00000000..f838854f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenImportWizardPage.java @@ -0,0 +1,525 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Tree; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.project.AbstractProjectScanner; +import org.eclipse.m2e.core.project.LocalProjectScanner; +import org.eclipse.m2e.core.project.MavenProjectInfo; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Maven Import Wizard Page + * + * @author Eugene Kuleshov + */ +public class MavenImportWizardPage extends AbstractMavenWizardPage { + + static final Object[] EMPTY = new Object[0]; + + protected Combo rootDirectoryCombo; + + protected CheckboxTreeViewer projectTreeViewer; + + private List<String> locations; + + private IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + + private WorkingSetGroup workingSetGroup; + + private boolean showLocation = true; + + protected MavenImportWizardPage(ProjectImportConfiguration importConfiguration) { + super("MavenProjectImportWizardPage", importConfiguration); + setTitle(org.eclipse.m2e.core.internal.Messages.MavenImportWizardPage_title); + setDescription(org.eclipse.m2e.core.internal.Messages.MavenImportWizardPage_desc); + setPageComplete(false); + } + + public void setShowLocation(boolean showLocation) { + this.showLocation = showLocation; + } + + public void setLocations(List<String> locations) { + this.locations = locations; + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(3, false)); + setControl(composite); + + if(showLocation || locations==null || locations.isEmpty()) { + final Label selectRootDirectoryLabel = new Label(composite, SWT.NONE); + selectRootDirectoryLabel.setLayoutData(new GridData()); + selectRootDirectoryLabel.setText(Messages.getString("wizard.import.page.root")); //$NON-NLS-1$ + + rootDirectoryCombo = new Combo(composite, SWT.NONE); + rootDirectoryCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + rootDirectoryCombo.addSelectionListener(new SelectionAdapter() { + public void widgetDefaultSelected(SelectionEvent e) { + if(rootDirectoryCombo.getText().trim().length() > 0) { + scanProjects(); + } + } + + public void widgetSelected(SelectionEvent e) { + if(rootDirectoryCombo.getText().trim().length() > 0) { + scanProjects(); + } + } + }); + rootDirectoryCombo.setFocus(); + addFieldWithHistory("rootDirectory", rootDirectoryCombo); //$NON-NLS-1$ + + if(locations!=null && locations.size()==1) { + rootDirectoryCombo.setText(locations.get(0)); + } + + final Button browseButton = new Button(composite, SWT.NONE); + browseButton.setText(Messages.getString("wizard.import.page.browse")); //$NON-NLS-1$ + browseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + browseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.NONE); + dialog.setText(Messages.getString("wizard.import.page.selectRootFolder")); //$NON-NLS-1$ + String path = rootDirectoryCombo.getText(); + if(path.length()==0) { + path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString(); + } + dialog.setFilterPath(path); + + String result = dialog.open(); + if(result != null) { + rootDirectoryCombo.setText(result); + scanProjects(); + } + } + }); + } + + final Label projectsLabel = new Label(composite, SWT.NONE); + projectsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); + projectsLabel.setText(Messages.getString("wizard.import.page.projects")); //$NON-NLS-1$ + + projectTreeViewer = new CheckboxTreeViewer(composite, SWT.BORDER); + + projectTreeViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + projectTreeViewer.setSubtreeChecked(event.getElement(), event.getChecked()); + updateCheckedState(); + Object[] checkedElements = projectTreeViewer.getCheckedElements(); + setPageComplete(checkedElements != null && checkedElements.length > 0); + } + }); + + projectTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + validateProjectInfo((MavenProjectInfo) selection.getFirstElement()); + }}); + + projectTreeViewer.setContentProvider(new ITreeContentProvider() { + + public Object[] getElements(Object element) { + if(element instanceof List) { + @SuppressWarnings("unchecked") + List<MavenProjectInfo> projects = (List<MavenProjectInfo>) element; + return projects.toArray(new MavenProjectInfo[projects.size()]); + } + return EMPTY; + } + + public Object[] getChildren(Object parentElement) { + if(parentElement instanceof List) { + @SuppressWarnings("unchecked") + List<MavenProjectInfo> projects = (List<MavenProjectInfo>) parentElement; + return projects.toArray(new MavenProjectInfo[projects.size()]); + } else if(parentElement instanceof MavenProjectInfo) { + MavenProjectInfo mavenProjectInfo = (MavenProjectInfo) parentElement; + Collection<MavenProjectInfo> projects = mavenProjectInfo.getProjects(); + return projects.toArray(new MavenProjectInfo[projects.size()]); + } + return EMPTY; + } + + public Object getParent(Object element) { + return null; + } + + public boolean hasChildren(Object parentElement) { + if(parentElement instanceof List) { + List<?> projects = (List<?>) parentElement; + return !projects.isEmpty(); + } else if(parentElement instanceof MavenProjectInfo) { + MavenProjectInfo mavenProjectInfo = (MavenProjectInfo) parentElement; + return !mavenProjectInfo.getProjects().isEmpty(); + } + return false; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + }); + + projectTreeViewer.setLabelProvider(new ProjectLabelProvider()); + + final Tree projectTree = projectTreeViewer.getTree(); + GridData projectTreeData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 3); + projectTreeData.heightHint = 250; + projectTreeData.widthHint = 500; + projectTree.setLayoutData(projectTreeData); + + final Button selectAllButton = new Button(composite, SWT.NONE); + selectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + selectAllButton.setText(Messages.getString("wizard.import.page.selectAll")); //$NON-NLS-1$ + selectAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + projectTreeViewer.expandAll(); + setAllChecked(true); + // projectTreeViewer.setSubtreeChecked(projectTreeViewer.getInput(), true); + validate(); + } + }); + + final Button deselectAllButton = new Button(composite, SWT.NONE); + deselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + deselectAllButton.setText(Messages.getString("wizard.import.page.deselectAll")); //$NON-NLS-1$ + deselectAllButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setAllChecked(false); + // projectTreeViewer.setSubtreeChecked(projectTreeViewer.getInput(), false); + setPageComplete(false); + } + }); + + final Button refreshButton = new Button(composite, SWT.NONE); + refreshButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true)); + refreshButton.setText(Messages.getString("wizard.import.page.refresh")); //$NON-NLS-1$ + refreshButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + scanProjects(); + } + }); + + this.workingSetGroup = new WorkingSetGroup(composite, getImportConfiguration(), getShell()); + + createAdvancedSettings(composite, new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + resolverConfigurationComponent.template.addModifyListener(new ModifyListener(){ + public void modifyText(ModifyEvent arg0) { + validate(); + } + }); + + if(locations!=null && !locations.isEmpty()) { + scanProjects(); + } + } + + public void dispose() { + super.dispose(); + workingSetGroup.dispose(); + } + + protected void scanProjects() { + final AbstractProjectScanner<MavenProjectInfo> projectScanner = getProjectScanner(); + try { + getWizard().getContainer().run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + projectScanner.run(monitor); + } + }); + + projectTreeViewer.setInput(projectScanner.getProjects()); + projectTreeViewer.expandAll(); + // projectTreeViewer.setAllChecked(true); + setAllChecked(true); + Object[] checkedElements = projectTreeViewer.getCheckedElements(); + setPageComplete(checkedElements != null && checkedElements.length > 0); + setErrorMessage(null); + setMessage(null); + + List<Throwable> errors = projectScanner.getErrors(); + if(!errors.isEmpty()) { + StringBuffer sb = new StringBuffer(Messages.getString("wizard.import.page.scanningErrors", errors.size())); //$NON-NLS-1$ + int n = 1; + for(Throwable ex : errors) { + if(ex instanceof CoreException) { + String msg = ((CoreException) ex).getStatus().getMessage(); + sb.append("\n ").append(n).append(" ").append(msg.trim()); //$NON-NLS-1$ //$NON-NLS-2$ + + } else { + String msg = ex.getMessage()==null ? ex.toString() : ex.getMessage(); + sb.append("\n ").append(n).append(" ").append(msg.trim()); //$NON-NLS-1$ //$NON-NLS-2$ + } + n++; + } + + setMessage(sb.toString(), IMessageProvider.WARNING); + } + + } catch(InterruptedException ex) { + // canceled + + } catch(InvocationTargetException ex) { + Throwable e = ex.getTargetException() == null ? ex : ex.getTargetException(); + String msg; + if(e instanceof CoreException) { + msg = e.getMessage(); + MavenLogger.log(msg, e); + } else { + msg = "Scanning error " + projectScanner.getDescription() + "; " + e.toString(); //$NON-NLS-2$ + MavenPlugin.getDefault().getConsole().logError(msg); + MavenLogger.log(msg, e); + } + projectTreeViewer.setInput(null); + setPageComplete(false); + setErrorMessage(msg); + + } + } + + void setAllChecked(boolean state) { + @SuppressWarnings("unchecked") + List<MavenProjectInfo> input = (List<MavenProjectInfo>) projectTreeViewer.getInput(); + if(input!=null) { + for(MavenProjectInfo mavenProjectInfo : input) { + projectTreeViewer.setSubtreeChecked(mavenProjectInfo, state); + } + updateCheckedState(); + } + } + + void updateCheckedState() { + Object[] elements = projectTreeViewer.getCheckedElements(); + for(int i = 0; i < elements.length; i++ ) { + Object element = elements[i]; + if(element instanceof MavenProjectInfo) { + MavenProjectInfo info = (MavenProjectInfo) element; + if(isWorkspaceFolder(info) || isAlreadyExists(info)) { + projectTreeViewer.setChecked(info, false); + } + } + } + } + + boolean isWorkspaceFolder(MavenProjectInfo info) { + if(info!=null) { + File pomFile = info.getPomFile(); + if(pomFile != null) { + File parentFile = pomFile.getParentFile(); + if(parentFile.getAbsolutePath().equals(workspaceRoot.getLocation().toFile().getAbsolutePath())) { + return true; + } + } + } + return false; + } + + boolean isAlreadyExists(MavenProjectInfo info) { + if(info!=null) { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IProject project = getImportConfiguration().getProject(workspace.getRoot(), info.getModel()); + return project.exists(); + } + return false; + } + + protected AbstractProjectScanner<MavenProjectInfo> getProjectScanner() { + File root = workspaceRoot.getLocation().toFile(); + MavenPlugin mavenPlugin = MavenPlugin.getDefault(); + MavenModelManager modelManager = mavenPlugin.getMavenModelManager(); + MavenConsole console = mavenPlugin.getConsole(); + if(showLocation || locations == null || locations.isEmpty()) { + return new LocalProjectScanner(root, rootDirectoryCombo.getText(), false, modelManager, console); + } + return new LocalProjectScanner(root, locations, true, modelManager, console); + } + + /** + * @return collection of <code>MavenProjectInfo</code> + */ + public Collection<MavenProjectInfo> getProjects() { + Collection<MavenProjectInfo> checkedProjects = new ArrayList<MavenProjectInfo>(); + for(Object o : projectTreeViewer.getCheckedElements()) { + checkedProjects.add((MavenProjectInfo) o); + } + + return checkedProjects; + } + + private void collectProjects(List<MavenProjectInfo> mavenProjects, Set<MavenProjectInfo> checkedProjects, + Collection<MavenProjectInfo> childProjects) { + for(MavenProjectInfo projectInfo : childProjects) { + if(checkedProjects.contains(projectInfo)) { + mavenProjects.add(projectInfo); + } else { + collectProjects(mavenProjects, checkedProjects, projectInfo.getProjects()); + } + } + } + + protected boolean validateProjectInfo(MavenProjectInfo info) { + if(info!=null) { + String projectName = getImportConfiguration().getProjectName(info.getModel()); + if(isWorkspaceFolder(info)) { + setMessage(Messages.getString("wizard.import.validator.workspaceFolder", projectName), IMessageProvider.WARNING); //$NON-NLS-1$ + } else if(isAlreadyExists(info)) { + setMessage(Messages.getString("wizard.import.validator.projectExists", projectName), IMessageProvider.WARNING); //$NON-NLS-1$ + } else { + setMessage(null, IMessageProvider.WARNING); + return false; + } + } + return true; + } + + protected void validate() { + Object[] elements = projectTreeViewer.getCheckedElements(); + for(int i = 0; i < elements.length; i++ ) { + Object element = elements[i]; + if(element instanceof MavenProjectInfo) { + if (validateProjectInfo((MavenProjectInfo) element)) { + setPageComplete(false); + return; + } + } + } + + setMessage(null); + setPageComplete(projectTreeViewer.getCheckedElements().length > 0); + projectTreeViewer.refresh(); + } + + /** + * ProjectLabelProvider + */ + class ProjectLabelProvider extends LabelProvider implements IColorProvider { + + public String getText(Object element) { + if(element instanceof MavenProjectInfo) { + MavenProjectInfo info = (MavenProjectInfo) element; + + if(info.getProfiles().isEmpty()) { + return info.getLabel() + " - " + getId(info); //$NON-NLS-1$ + } + + return info.getLabel() + " - " + getId(info) + " " + info.getProfiles(); //$NON-NLS-1$ //$NON-NLS-2$ + } + return super.getText(element); + } + + private String getId(MavenProjectInfo info) { + Model model = info.getModel(); + + String groupId = model.getGroupId(); + String artifactId = model.getArtifactId(); + String version = model.getVersion(); + String packaging = model.getPackaging(); + + Parent parent = model.getParent(); + + if(groupId==null && parent!=null) { + groupId = parent.getGroupId(); + } + if(groupId==null) { + groupId = org.eclipse.m2e.core.internal.Messages.MavenImportWizardPage_inherited; + } + + if(version==null && parent!=null) { + version = parent.getVersion(); + } + if(version==null) { + version = org.eclipse.m2e.core.internal.Messages.MavenImportWizardPage_inherited; + } + + return groupId + ":" + artifactId + ":" + version + ":" + packaging; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object) + */ + public Color getForeground(Object element) { + if(element instanceof MavenProjectInfo) { + MavenProjectInfo info = (MavenProjectInfo) element; + if(isWorkspaceFolder(info)) { + return Display.getDefault().getSystemColor(SWT.COLOR_RED); + } else if(isAlreadyExists(info)) { + return Display.getDefault().getSystemColor(SWT.COLOR_GRAY); + } + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object) + */ + public Color getBackground(Object element) { + return null; + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileArtifactWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileArtifactWizardPage.java new file mode 100644 index 00000000..adbcd2a0 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileArtifactWizardPage.java @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.project.MavenProject; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Wizard page to enter parameters required for artifact installation. + * + * @author Guillaume Sauthier + * @author Mike Haller + * @author Eugene Kuleshov + */ +public class MavenInstallFileArtifactWizardPage extends WizardPage { + + Text artifactFileNameText; + Text pomFileNameText; + + private Combo groupIdCombo; + private Combo artifactIdCombo; + private Combo versionCombo; + private Combo packagingCombo; + private Combo classifierCombo; + + Button createChecksumButton; + Button generatePomButton; + + private final IFile file; + + public MavenInstallFileArtifactWizardPage(IFile file) { + super("mavenInstallFileWizardPage"); + this.file = file; + this.setTitle(Messages.MavenInstallFileArtifactWizardPage_title); + this.setDescription(Messages.MavenInstallFileArtifactWizardPage_desc); + } + + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(3, false)); + container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + ModifyListener modifyingListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + pageChanged(); + } + }; + + Label artifactFileNameLabel = new Label(container, SWT.NONE); + artifactFileNameLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblFileName); + + artifactFileNameText = new Text(container, SWT.BORDER); + artifactFileNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + artifactFileNameText.setData("name", "artifactFileNametext"); //$NON-NLS-1$ //$NON-NLS-2$ + artifactFileNameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateFileName(getArtifactFileName()); + pageChanged(); + } + }); + + final Button artifactFileNameButton = new Button(container, SWT.NONE); + artifactFileNameButton.setLayoutData(new GridData()); + artifactFileNameButton.setData("name", "externalPomFileButton"); //$NON-NLS-1$ //$NON-NLS-2$ + artifactFileNameButton.setText(Messages.MavenInstallFileArtifactWizardPage_btnFilename); + artifactFileNameButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + FileDialog fileDialog = new FileDialog(artifactFileNameButton.getShell()); + fileDialog.setText(Messages.MavenInstallFileArtifactWizardPage_file_title); + fileDialog.setFileName(artifactFileNameText.getText()); + String name = fileDialog.open(); + if(name!=null) { + updateFileName(name); + } + } + }); + + Label pomFileNameLabel = new Label(container, SWT.NONE); + pomFileNameLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblPom); + + pomFileNameText = new Text(container, SWT.BORDER); + pomFileNameText.setData("name", "pomFileNameText"); //$NON-NLS-1$ //$NON-NLS-2$ + pomFileNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + pomFileNameText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + generatePomButton.setSelection(getPomFileName().length()==0); + pageChanged(); + } + }); + + final Button pomFileNameButton = new Button(container, SWT.NONE); + pomFileNameButton.setLayoutData(new GridData()); + pomFileNameButton.setData("name", "externalPomFileButton"); //$NON-NLS-1$ //$NON-NLS-2$ + pomFileNameButton.setText(Messages.MavenInstallFileArtifactWizardPage_btnPom); + pomFileNameButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + FileDialog fileDialog = new FileDialog(pomFileNameButton.getShell()); + fileDialog.setText(Messages.MavenInstallFileArtifactWizardPage_file_title); + fileDialog.setFileName(pomFileNameText.getText()); + String res = fileDialog.open(); + if(res!=null) { + pomFileNameText.setText(res); + } + } + }); + + new Label(container, SWT.NONE); + + generatePomButton = new Button(container, SWT.CHECK); + generatePomButton.setData("name", "generatePomButton"); //$NON-NLS-1$ //$NON-NLS-2$ + generatePomButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + generatePomButton.setText(Messages.MavenInstallFileArtifactWizardPage_btnGenerate); + generatePomButton.setSelection(true); + new Label(container, SWT.NONE); + + createChecksumButton = new Button(container, SWT.CHECK); + createChecksumButton.setData("name", "createChecksumButton"); //$NON-NLS-1$ //$NON-NLS-2$ + createChecksumButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + createChecksumButton.setText(Messages.MavenInstallFileArtifactWizardPage_btnChecksum); + createChecksumButton.setSelection(true); + + Label separator = new Label(container, SWT.HORIZONTAL | SWT.SEPARATOR); + GridData separatorData = new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1); + separatorData.verticalIndent = 5; + separator.setLayoutData(separatorData); + + Label groupIdlabel = new Label(container, SWT.NONE); + groupIdlabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblgroupid); + + groupIdCombo = new Combo(container, SWT.NONE); + groupIdCombo.setData("name", "groupIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + groupIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + groupIdCombo.addModifyListener(modifyingListener); + new Label(container, SWT.NONE); + + Label artifactIdLabel = new Label(container, SWT.NONE); + artifactIdLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblArtifact); + + artifactIdCombo = new Combo(container, SWT.NONE); + artifactIdCombo.setData("name", "artifactIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + artifactIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + artifactIdCombo.addModifyListener(modifyingListener); + new Label(container, SWT.NONE); + + Label versionLabel = new Label(container, SWT.NONE); + versionLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblVersion); + + versionCombo = new Combo(container, SWT.NONE); + versionCombo.setData("name", "versionCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + versionCombo.setText(MavenArtifactComponent.DEFAULT_VERSION); + GridData versionComboData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + versionComboData.widthHint = 150; + versionCombo.setLayoutData(versionComboData); + versionCombo.addModifyListener(modifyingListener); + + Label packagingLabel = new Label(container, SWT.NONE); + packagingLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblPackaging); + + packagingCombo = new Combo(container, SWT.NONE); + packagingCombo.setData("name", "packagingCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + packagingCombo.setText(MavenArtifactComponent.DEFAULT_PACKAGING); + packagingCombo.setItems(MavenArtifactComponent.PACKAGING_OPTIONS); + GridData packagingComboData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + packagingComboData.widthHint = 150; + packagingCombo.setLayoutData(packagingComboData); + packagingCombo.addModifyListener(modifyingListener); + + Label classifierLabel = new Label(container, SWT.NONE); + classifierLabel.setText(Messages.MavenInstallFileArtifactWizardPage_lblClassifier); + + classifierCombo = new Combo(container, SWT.NONE); + classifierCombo.setData("name", "classifierText"); //$NON-NLS-1$ //$NON-NLS-2$ + classifierCombo.setItems(new String[] {"sources", "javadoc"}); //$NON-NLS-1$ //$NON-NLS-2$ + GridData classifierTextData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + classifierTextData.widthHint = 150; + classifierCombo.setLayoutData(classifierTextData); + classifierCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + generatePomButton.setSelection(getClassifier().length()==0); + } + }); + + if(file != null) { + updateFileName(file.getLocation().toOSString()); + } + + setControl(container); + } + + void updateFileName(String fileName) { + if(!getArtifactFileName().equals(fileName)) { + artifactFileNameText.setText(fileName); + } + + File file = new File(fileName); + if(!file.exists() || !file.isFile()) { + return; + } + + if(fileName.endsWith(".jar") || fileName.endsWith(".war") || fileName.endsWith(".ear")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + packagingCombo.setText(fileName.substring(fileName.length()-3)); + } + + int n = fileName.lastIndexOf('.'); + if(n>-1) { + String pomFileName = fileName.substring(0, n) + ".pom"; //$NON-NLS-1$ + if(new File(pomFileName).exists()) { + pomFileNameText.setText(pomFileName); + } + } else { + pomFileNameText.setText(""); //$NON-NLS-1$ + } + + MavenPlugin plugin = MavenPlugin.getDefault(); + try { + IndexedArtifactFile iaf = plugin.getIndexManager().getAllIndexes().identify(file); + if(iaf!=null) { + groupIdCombo.setText(iaf.group); + artifactIdCombo.setText(iaf.artifact); + versionCombo.setText(iaf.version); + if(iaf.classifier!=null) { + classifierCombo.setText(iaf.classifier); + } + + String name = iaf.group + ":" + iaf.artifact + "-" + iaf.version // //$NON-NLS-1$ //$NON-NLS-2$ + + (iaf.classifier == null ? "" : iaf.classifier); //$NON-NLS-1$ + setMessage(NLS.bind(Messages.MavenInstallFileArtifactWizardPage_message, name), WARNING); + return; + } + } catch(CoreException ex) { + MavenLogger.log(ex); + } + + if(n>-1) { + String pomFileName = fileName.substring(0, n) + ".pom"; //$NON-NLS-1$ + if(new File(pomFileName).exists()) { + pomFileNameText.setText(pomFileName); + + // read pom file + + try { + IMaven maven = MavenPlugin.getDefault().getMaven(); + MavenProject mavenProject = maven.readProject(new File(pomFileName), null); + + groupIdCombo.setText(mavenProject.getGroupId()); + artifactIdCombo.setText(mavenProject.getArtifactId()); + versionCombo.setText(mavenProject.getVersion()); + packagingCombo.setText(mavenProject.getPackaging()); + return; + + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + } + + ArtifactKey artifactKey = SelectionUtil.getType(file, ArtifactKey.class); + if(artifactKey!=null) { + groupIdCombo.setText(artifactKey.getGroupId()); + artifactIdCombo.setText(artifactKey.getArtifactId()); + versionCombo.setText(artifactKey.getVersion()); + if(artifactKey.getClassifier()!=null) { + classifierCombo.setText(artifactKey.getClassifier()); + } + } + } + + void pageChanged() { + String artifactFileName = getArtifactFileName(); + if(artifactFileName.length() == 0) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_no_name); + return; + } + + File file = new File(artifactFileName); + if(!file.exists() || !file.isFile()) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_missing); + return; + } + + String pomFileName = getPomFileName(); + if(pomFileName.length()>0) { + if(!new File(pomFileName).exists()) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_missingpom); + return; + } + } + + if(getGroupId().length() == 0) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_groupid); + return; + } + + if(getArtifactId().length() == 0) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_artifactid); + return; + } + + if(getVersion().length() == 0) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_version); + return; + } + + if(getPackaging().length() == 0) { + updateStatus(Messages.MavenInstallFileArtifactWizardPage_error_packaging); + return; + } + + updateStatus(null); + } + + private void updateStatus(String message) { + setErrorMessage(message); + setPageComplete(message == null); + } + + public String getArtifactFileName() { + return artifactFileNameText.getText().trim(); + } + + public String getPomFileName() { + return pomFileNameText.getText().trim(); + } + + public String getGroupId() { + return groupIdCombo.getText().trim(); + } + + public String getArtifactId() { + return artifactIdCombo.getText().trim(); + } + + public String getVersion() { + return versionCombo.getText().trim(); + } + + public String getPackaging() { + return packagingCombo.getText().trim(); + } + + public String getClassifier() { + return this.classifierCombo.getText().trim(); + } + + public boolean isGeneratePom() { + return generatePomButton.getSelection(); + } + + public boolean isCreateChecksum() { + return createChecksumButton.getSelection(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileRepositoryWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileRepositoryWizardPage.java new file mode 100644 index 00000000..ef199a8f --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileRepositoryWizardPage.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.emf.common.util.EList; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +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.Composite; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.model.edit.pom.PomFactory; +import org.eclipse.m2e.model.edit.pom.Repository; + + +/** + * MavenInstallFileRepositoryWizardPage used to chose to repositories for installing artifacts. + * + * @author Mike Haller + */ +public class MavenInstallFileRepositoryWizardPage extends WizardPage { + + private final IFile pomFile; + + public MavenInstallFileRepositoryWizardPage(IFile pomFile) { + super("mavenInstallFileRepositorySelectionPage"); + setTitle("Repository Selection Page"); + setDescription("Select the repositories where to deploy the artifact"); + this.pomFile = pomFile; + } + + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(1, false)); + container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Label repositoriesLabel = new Label(container, SWT.NONE); + repositoriesLabel.setData("name", "repositoriesLabel"); + repositoriesLabel.setText("&Repositories:"); + + CheckboxTreeViewer repositoryViewer = new CheckboxTreeViewer(container, SWT.BORDER); + repositoryViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + // repositoryViewer.setInput(pomFile); + repositoryViewer.setLabelProvider(new RepositoryLabelProvider()); + repositoryViewer.setContentProvider(new RepositoriesContentProvider()); + repositoryViewer.setCheckedElements(new Object[] {RepositoriesContentProvider.LOCAL_REPOSITORY}); + + initRepositories(); + + setControl(container); + } + + private void initRepositories() { + // TODO Auto-generated method initRepositories + + } + + /** + * RepositoryLabelProvider prints the name of a repository, and if available, its URL. + */ + public class RepositoryLabelProvider extends LabelProvider { + + public String getText(Object element) { + if(element instanceof Repository) { + Repository repository = (Repository) element; + if(repository.getUrl() != null && repository.getUrl().trim().length() > 0) { + return repository.getName() + "(" + repository.getUrl() + ")"; + } + return repository.getName(); + } + return super.getText(element); + } + + } + + /** + * LocalRepositoriesContentProvider provides a list of local and remote repositories. + * <p> + * If no repositories could be found in the POM Maven Model, a default local repository is shown. + */ + public static class RepositoriesContentProvider implements ITreeContentProvider { + + public static final Repository LOCAL_REPOSITORY = createLocalRepository(); + + private static Repository createLocalRepository() { + Repository repository = PomFactory.eINSTANCE.createRepository(); + repository.setId("local"); + repository.setName("Local Repository"); + try { + File localRepositoryDir = new File(MavenPlugin.getDefault().getMaven().getLocalRepository().getBasedir()); + repository.setUrl(localRepositoryDir.toURI().toString()); + } catch(CoreException ex) { + MavenLogger.log("Unable to determine local repository URL, using default", ex); + } + return repository; + } + + public Object[] getElements(Object arg0) { + return repositories.toArray(); + } + + private EList<Repository> repositories; + + public Object[] getChildren(Object arg0) { + return null; + } + + public Object getParent(Object arg0) { + return null; + } + + public boolean hasChildren(Object arg0) { + return false; + } + + public void dispose() { + } + + public void inputChanged(Viewer arg0, Object arg1, Object newInput) { + if(newInput instanceof Model) { + Model model = (Model) newInput; + repositories = model.getRepositories(); + } + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileWizard.java new file mode 100644 index 00000000..23212884 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenInstallFileWizard.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.progress.IProgressConstants; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionResult; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Wizard to install artifacts into the local Maven repository. + * + * @author Guillaume Sauthier + * @author Mike Haller + * @author Eugene Kuleshov + * @since 0.9.7 + */ +public class MavenInstallFileWizard extends Wizard implements IImportWizard { + + private IFile selectedFile; + + private IFile pomFile; + + private MavenInstallFileArtifactWizardPage artifactPage; + + private MavenInstallFileRepositoryWizardPage repositoryPage; + + public MavenInstallFileWizard() { + setNeedsProgressMonitor(true); + setWindowTitle(Messages.MavenInstallFileWizard_title); + } + + public void addPages() { + artifactPage = new MavenInstallFileArtifactWizardPage(selectedFile); + addPage(artifactPage); + + // repositoryPage = new MavenInstallFileRepositoryWizardPage(pomFile); + // addPage(repositoryPage); + } + + public boolean performFinish() { + final Properties properties = new Properties(); + + // Mandatory Properties for install:install-file + properties.setProperty("file", artifactPage.getArtifactFileName()); //$NON-NLS-1$ + + properties.setProperty("groupId", artifactPage.getGroupId()); //$NON-NLS-1$ + properties.setProperty("artifactId", artifactPage.getArtifactId()); //$NON-NLS-1$ + properties.setProperty("version", artifactPage.getVersion()); //$NON-NLS-1$ + properties.setProperty("packaging", artifactPage.getPackaging()); //$NON-NLS-1$ + + if(artifactPage.getClassifier().length()>0) { + properties.setProperty("classifier", artifactPage.getClassifier()); //$NON-NLS-1$ + } + + if(artifactPage.getPomFileName().length()>0) { + properties.setProperty("pomFile", artifactPage.getPomFileName()); //$NON-NLS-1$ + } + if(artifactPage.isGeneratePom()) { + properties.setProperty("generatePom", "true"); //$NON-NLS-1$ //$NON-NLS-2$ + } + if(artifactPage.isCreateChecksum()) { + properties.setProperty("createChecksum", "true"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + new Job(Messages.MavenInstallFileWizard_job) { + protected IStatus run(IProgressMonitor monitor) { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + MavenPlugin plugin = MavenPlugin.getDefault(); + try { + // Run the install:install-file goal + IMaven maven = MavenPlugin.getDefault().getMaven(); + MavenExecutionRequest request = maven.createExecutionRequest(monitor); + request.setGoals(Arrays.asList("install:install-file")); //$NON-NLS-1$ + request.setUserProperties(properties); + MavenExecutionResult executionResult = maven.execute(request, monitor); + + List<Throwable> exceptions = executionResult.getExceptions(); + if(!exceptions.isEmpty()) { + for(Throwable exception : exceptions) { + String msg = Messages.MavenInstallFileWizard_error; + plugin.getConsole().logError(msg + "; " + exception.toString()); //$NON-NLS-1$ + MavenLogger.log(msg, exception); + } + } + + // TODO update index for local maven repository + + } catch (CoreException ex) { + MavenLogger.log(ex); + plugin.getConsole().logError("Failed to install artifact"); + } + return Status.OK_STATUS; + } + }.schedule(); + + return true; + } + + public void init(IWorkbench workbench, IStructuredSelection selection) { + Object element = selection.getFirstElement(); + if(element instanceof IFile) { + selectedFile = (IFile) element; + setPomFile(selectedFile.getProject()); + } else if(element instanceof IProject) { + setPomFile((IProject) element); + } + } + + private void setPomFile(IProject project) { + if(project.isAccessible()) { + IFile pomFile = project.getFile(IMavenConstants.POM_FILE_NAME); + if(pomFile!=null && pomFile.isAccessible()) { + this.pomFile = pomFile; + } + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenLocationComponent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenLocationComponent.java new file mode 100644 index 00000000..534dcdfe --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenLocationComponent.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.m2e.core.core.Messages; + + +/** + * Simple GUI component which allows the user to choose between a workspace + * location and a user specified external location. + * + * This component is mainly used for choosing the location at which to create + * a new project. + */ +public class MavenLocationComponent extends Composite { + + /** Radio button indicating whether the workspace location has been chosen. */ + protected Button inWorkspaceButton; + + /** Radio button indicating whether an external location has been chosen. */ + protected Button inExternalLocationButton; + + /** Text field for defining a user specified external location. */ + protected Combo locationCombo; + + /** Button allowing to choose a directory on the file system as the external location. */ + protected Button locationBrowseButton; + + protected ModifyListener modifyingListener; + + protected Label locationLabel; + + /** + * Constructor. + * + * Constructs all the GUI components contained in this <code>Composite</code>. + * These components allow the user to choose between a workspace location and + * a user specified external location. + * + * @param parent The widget which will be the parent of this component. + * @param styles The widget style for this component. + * @param modifyingListener Listener which is notified when the contents of + * this component change due to user input. + */ + public MavenLocationComponent( final Composite parent, int styles ) { + super( parent, styles ); + + GridLayout gridLayout = new GridLayout(); + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + setLayout( gridLayout ); + + Group locationGroup = new Group( this, SWT.NONE ); + locationGroup.setText( Messages.getString( "locationComponent.location" ) ); //$NON-NLS-1$ + locationGroup.setLayoutData( new GridData( GridData.FILL, GridData.FILL, true, true, 3, 1 ) ); + GridLayout groupLayout = new GridLayout(); + groupLayout.numColumns = 3; + groupLayout.marginLeft = 0; + locationGroup.setLayout( groupLayout ); + + GridData gridData = new GridData(); + gridData.horizontalSpan = 3; + + // first radio button + inWorkspaceButton = new Button( locationGroup, SWT.RADIO ); + inWorkspaceButton.setText( Messages.getString( "locationComponent.inWorkspace" ) ); //$NON-NLS-1$ + inWorkspaceButton.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1) ); + inWorkspaceButton.addSelectionListener( new SelectionAdapter() { + public void widgetSelected( SelectionEvent e ) { + boolean isEnabled = !inWorkspaceButton.getSelection(); + locationLabel.setEnabled( isEnabled ); + locationCombo.setEnabled( isEnabled ); + locationBrowseButton.setEnabled( isEnabled ); + if(modifyingListener!=null) { + modifyingListener.modifyText( null ); + } + } + } ); + + // second radio button + inExternalLocationButton = new Button( locationGroup, SWT.RADIO ); + inExternalLocationButton.setText( Messages.getString( "locationComponent.atExternal" ) ); //$NON-NLS-1$ + inExternalLocationButton.setLayoutData( gridData ); + + // choose directory + locationLabel = new Label( locationGroup, SWT.NONE ); + GridData gd_locationLabel = new GridData(); + gd_locationLabel.horizontalIndent = 10; + locationLabel.setLayoutData(gd_locationLabel); + locationLabel.setText( Messages.getString( "locationComponent.directory" ) ); //$NON-NLS-1$ + + locationCombo = new Combo( locationGroup, SWT.BORDER ); + locationCombo.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) ); + + locationBrowseButton = new Button( locationGroup, SWT.PUSH ); + locationBrowseButton.setText( Messages.getString( "locationComponent.browse" ) ); //$NON-NLS-1$ + + gridData = new GridData( SWT.FILL, SWT.DEFAULT, false, false ); + locationBrowseButton.setLayoutData( gridData ); + + locationBrowseButton.addSelectionListener( new SelectionAdapter() { + public void widgetSelected( SelectionEvent e ) { + DirectoryDialog dialog = new DirectoryDialog(getShell()); + dialog.setText(Messages.getString( "locationComponent.selectLocation" )); //$NON-NLS-1$ + + String path = locationCombo.getText(); + if(path.length()==0) { + path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString(); + } + dialog.setFilterPath(path); + + String selectedDir = dialog.open(); + if(selectedDir != null) { + locationCombo.setText( selectedDir.trim() ); + } + } + } ); + + inWorkspaceButton.setSelection( true ); + + locationLabel.setEnabled( false ); + locationCombo.setEnabled( false ); + locationBrowseButton.setEnabled( false ); + } + + /** + * Returns the path of the location chosen by the user. + * + * According to the user input, the path either points to the workspace or + * to a valid user specified location on the filesystem. + * + * @return The path of the location chosen by the user. + * Is never <code>null</code>. + */ + public IPath getLocationPath() { + if ( isInWorkspace() ) { + return Platform.getLocation(); + } + return Path.fromOSString( locationCombo.getText().trim() ); + } + + /** + * Returns whether the workspace has been chosen as the location to use. + * + * @return <code>true</code> if the workspace is chosen as the location to use, + * <code>false</code> if the specified external location is to be used. + */ + public boolean isInWorkspace() { + return inWorkspaceButton.getSelection(); + } + + public void setModifyingListener( ModifyListener modifyingListener ) { + this.modifyingListener = modifyingListener; + locationCombo.addModifyListener( modifyingListener ); + } + + public Combo getLocationCombo() { + return locationCombo; + } + + public void dispose() { + super.dispose(); + if(modifyingListener!=null) { + locationCombo.removeModifyListener( modifyingListener ); + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenMaterializePomWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenMaterializePomWizard.java new file mode 100644 index 00000000..920ab4ef --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenMaterializePomWizard.java @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; + +import org.apache.maven.model.Dependency; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.MavenProjectPomScanner; +import org.eclipse.m2e.core.project.MavenProjectScmInfo; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * A wizard used to import projects for Maven artifacts + * + * @author Eugene Kuleshov + */ +public class MavenMaterializePomWizard extends Wizard implements IImportWizard, INewWizard { + + ProjectImportConfiguration importConfiguration; + + MavenDependenciesWizardPage selectionPage; + + MavenProjectWizardLocationPage locationPage; + + Button checkOutAllButton; + + Button useDeveloperConnectionButton; + + // TODO replace with ArtifactKey + private Dependency[] dependencies; + + private IStructuredSelection selection; + + + public MavenMaterializePomWizard() { + importConfiguration = new ProjectImportConfiguration(); + setNeedsProgressMonitor(true); + setWindowTitle(Messages.MavenMaterializePomWizard_title); + } + + public void setDependencies(Dependency[] dependencies) { + this.dependencies = dependencies; + } + + public Dependency[] getDependencies() { + return dependencies; + } + + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + + importConfiguration.setWorkingSet(SelectionUtil.getSelectedWorkingSet(selection)); + + ArrayList<Dependency> dependencies = new ArrayList<Dependency>(); + + for(Iterator<?> it = selection.iterator(); it.hasNext();) { + Object element = it.next(); + ArtifactKey artifactKey = SelectionUtil.getType(element, ArtifactKey.class); + if(artifactKey!=null) { + Dependency d = new Dependency(); + d.setGroupId(artifactKey.getGroupId()); + d.setArtifactId(artifactKey.getArtifactId()); + d.setVersion(artifactKey.getVersion()); + d.setClassifier(artifactKey.getClassifier()); + dependencies.add(d); + } + } + + setDependencies(dependencies.toArray(new Dependency[dependencies.size()])); + } + + public void addPages() { + selectionPage = new MavenDependenciesWizardPage(importConfiguration, // + Messages.MavenMaterializePomWizard_dialog_title, // + Messages.MavenMaterializePomWizard_dialog_message) { + protected void createAdvancedSettings(Composite composite, GridData gridData) { + checkOutAllButton = new Button(composite, SWT.CHECK); + checkOutAllButton.setText(Messages.MavenMaterializePomWizard_btnCheckout); + checkOutAllButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + + useDeveloperConnectionButton = new Button(composite, SWT.CHECK); + useDeveloperConnectionButton.setText(Messages.MavenMaterializePomWizard_btnDev); + useDeveloperConnectionButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + + super.createAdvancedSettings(composite, gridData); + } + }; + selectionPage.setDependencies(dependencies); + + locationPage = new MavenProjectWizardLocationPage(importConfiguration, // + Messages.MavenMaterializePomWizard_location_title, + Messages.MavenMaterializePomWizard_location_message); + locationPage.setLocationPath(SelectionUtil.getSelectedLocation(selection)); + + addPage(selectionPage); + addPage(locationPage); + } + + public boolean canFinish() { + return super.canFinish(); + } + + public boolean performFinish() { + if(!canFinish()) { + return false; + } + + final Dependency[] dependencies = selectionPage.getDependencies(); + + final boolean checkoutAllProjects = checkOutAllButton.getSelection(); + final boolean developer = useDeveloperConnectionButton.getSelection(); + + MavenProjectCheckoutJob job = new MavenProjectCheckoutJob(importConfiguration, checkoutAllProjects) { + protected List<MavenProjectScmInfo> getProjects(IProgressMonitor monitor) throws InterruptedException { + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenProjectPomScanner<MavenProjectScmInfo> scanner = new MavenProjectPomScanner<MavenProjectScmInfo>(developer, dependencies, // + plugin.getMavenModelManager(), // + plugin.getConsole()); + scanner.run(monitor); + // XXX handle errors/warnings + + return scanner.getProjects(); + } + }; + + if(!locationPage.isInWorkspace()) { + job.setLocation(locationPage.getLocationPath().toFile()); + } + + job.schedule(); + + return true; + } + +// public Scm[] getScms(IProgressMonitor monitor) { +// ArrayList scms = new ArrayList(); +// +// MavenPlugin plugin = MavenPlugin.getDefault(); +// MavenEmbedderManager embedderManager = plugin.getMavenEmbedderManager(); +// IndexManager indexManager = plugin.getMavenRepositoryIndexManager(); +// MavenConsole console = plugin.getConsole(); +// +// MavenEmbedder embedder = embedderManager.getWorkspaceEmbedder(); +// +// for(int i = 0; i < dependencies.length; i++ ) { +// try { +// Dependency d = dependencies[i]; +// +// Artifact artifact = embedder.createArtifact(d.getGroupId(), // +// d.getArtifactId(), d.getVersion(), null, "pom"); +// +// List remoteRepositories = indexManager.getArtifactRepositories(null, null); +// +// embedder.resolve(artifact, remoteRepositories, embedder.getLocalRepository()); +// +// File file = artifact.getFile(); +// if(file != null) { +// MavenProject project = embedder.readProject(file); +// +// Scm scm = project.getScm(); +// if(scm == null) { +// String msg = project.getId() + " doesn't specify SCM info"; +// console.logError(msg); +// continue; +// } +// +// String connection = scm.getConnection(); +// String devConnection = scm.getDeveloperConnection(); +// String tag = scm.getTag(); +// String url = scm.getUrl(); +// +// console.logMessage(project.getArtifactId()); +// console.logMessage("Connection: " + connection); +// console.logMessage(" dev: " + devConnection); +// console.logMessage(" url: " + url); +// console.logMessage(" tag: " + tag); +// +// if(connection==null) { +// if(devConnection==null) { +// String msg = project.getId() + " doesn't specify SCM connection"; +// console.logError(msg); +// continue; +// } +// scm.setConnection(devConnection); +// } +// +// // connection: scm:svn:https://svn.apache.org/repos/asf/incubator/wicket/branches/wicket-1.2.x/wicket +// // dev: scm:svn:https://svn.apache.org/repos/asf/incubator/wicket/branches/wicket-1.2.x/wicket +// // url: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.2.x/wicket +// // tag: HEAD +// +// // TODO add an option to select all modules/projects and optimize scan +// +// scms.add(scm); +// +//// if(!connection.startsWith(SCM_SVN_PROTOCOL)) { +//// String msg = project.getId() + " SCM type is not supported " + connection; +//// console.logError(msg); +//// addError(new Exception(msg)); +//// } else { +//// String svnUrl = connection.trim().substring(SCM_SVN_PROTOCOL.length()); +//// } +// } +// +// } catch(Exception ex) { +// console.logError(ex.getMessage()); +// } +// } +// +// return (Scm[]) scms.toArray(new Scm[scms.size()]); +// } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizard.java new file mode 100644 index 00000000..d9028c09 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizard.java @@ -0,0 +1,305 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.progress.IProgressConstants; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * A project wizard for creating a new Maven2 module project. + */ +public class MavenModuleWizard extends Wizard implements INewWizard { + + /** The name of the default wizard page image. */ + // protected static final String DEFAULT_PAGE_IMAGE_NAME = "icons/new_m2_project_wizard.gif"; + + /** The default wizard page image. */ + // protected static final ImageDescriptor DEFAULT_PAGE_IMAGE = MavenPlugin.getImageDescriptor(DEFAULT_PAGE_IMAGE_NAME); + + /** the current selection */ + private IStructuredSelection selection; + + /** the parent page (#1) */ + protected MavenModuleWizardParentPage parentPage; + + /** The archetype selection page. */ + protected MavenProjectWizardArchetypePage archetypePage; + + /** The wizard page for gathering Maven2 project information. */ + protected MavenProjectWizardArtifactPage artifactPage; + + /** The wizard page for gathering archetype project information. */ + protected MavenProjectWizardArchetypeParametersPage parametersPage; + + /** The wizard page for choosing the Maven2 dependencies to use. */ + private MavenDependenciesWizardPage dependenciesPage; + + /** The project configuration bean. */ + protected ProjectImportConfiguration configuration; + + private String moduleName; + + protected boolean isEditor = false; + + /** Default constructor. Sets the title and image of the wizard. */ + public MavenModuleWizard() { + setWindowTitle(Messages.getString("wizard.module.title")); //$NON-NLS-1$ + setDefaultPageImageDescriptor(MavenImages.WIZ_NEW_PROJECT); + setNeedsProgressMonitor(true); + } + + public MavenModuleWizard(boolean isEditor) { + this(); + this.isEditor = isEditor; + } + + /** Creates the pages. */ + public void addPages() { + configuration = new ProjectImportConfiguration(); + + parentPage = new MavenModuleWizardParentPage(configuration); + archetypePage = new MavenProjectWizardArchetypePage(configuration); + parametersPage = new MavenProjectWizardArchetypeParametersPage(configuration); + artifactPage = new MavenProjectWizardArtifactPage(configuration); + dependenciesPage = new MavenDependenciesWizardPage(configuration, // + Messages.getString("wizard.project.page.dependencies.title"), // //$NON-NLS-1$ + Messages.getString("wizard.project.page.dependencies.description")); //$NON-NLS-1$ + dependenciesPage.setDependencies(new Dependency[0]); + dependenciesPage.setShowScope(true); + + addPage(parentPage); + addPage(archetypePage); + addPage(parametersPage); + addPage(artifactPage); + addPage(dependenciesPage); + } + + /** Adds the listeners after the page controls are created. */ + public void createPageControls(Composite pageContainer) { + artifactPage.setParentReadonly(true); + artifactPage.setTitle(Messages.getString("wizard.module.page.artifact.title")); //$NON-NLS-1$ + archetypePage.setTitle(Messages.getString("wizard.module.page.archetype.title")); //$NON-NLS-1$ + parametersPage.setTitle(Messages.getString("wizard.module.page.parameters.title")); //$NON-NLS-1$ + + super.createPageControls(pageContainer); + + parametersPage.setArtifactIdEnabled(false); + + parentPage.addArchetypeSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + archetypePage.setUsed(!parentPage.isSimpleProject()); + } + }); + + parentPage.addModuleNameListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + parametersPage.setProjectName(parentPage.getModuleName()); + artifactPage.setProjectName(parentPage.getModuleName()); + } + }); + + parentPage.addParentProjectListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + copyParentValues(); + } + }); + + if(selection != null && selection.size() > 0) { + parentPage.setParent(selection.getFirstElement()); + copyParentValues(); + } + } + + /** Copies the parent project parameters to the artifact page. */ + protected void copyParentValues() { + Model model = parentPage.getParentModel(); + if(model != null) { + String groupId = model.getGroupId(); + String artifactId = model.getArtifactId(); + String version = model.getVersion(); + + if(groupId == null) { + Parent parent = model.getParent(); + if(parent != null) { + groupId = parent.getGroupId(); + } + } + if(version == null) { + Parent parent = model.getParent(); + if(parent != null) { + version = parent.getVersion(); + } + } + + artifactPage.setParentProject(groupId, artifactId, version); + parametersPage.setParentProject(groupId, artifactId, version); + } + } + + /** Stores the current selection. */ + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + } + + /** Performs the "finish" action. */ + public boolean performFinish() { + // First of all, we extract all the information from the wizard pages. + // Note that this should not be done inside the operation we will run + // since many of the wizard pages' methods can only be invoked from within + // the SWT event dispatcher thread. However, the operation spawns a new + // separate thread to perform the actual work, i.e. accessing SWT elements + // from within that thread would lead to an exception. + + final String moduleName = parentPage.getModuleName(); + + // Get the location where to create the project. For some reason, when using + // the default workspace location for a project, we have to pass null + // instead of the actual location. + final IPath location = parentPage.getParentContainer().getLocation(); + + final IFile parentPom = parentPage.getPom(); + + Job job; + + final MavenPlugin plugin = MavenPlugin.getDefault(); + + if(parentPage.isSimpleProject()) { + + final Model model = artifactPage.getModel(); + @SuppressWarnings("unchecked") + List<Dependency> modelDependencies = model.getDependencies(); + modelDependencies.addAll(Arrays.asList(dependenciesPage.getDependencies())); + + final String[] folders = artifactPage.getFolders(); + + job = new WorkspaceJob(Messages.getString("wizard.project.job.creatingProject", moduleName)) { //$NON-NLS-1$ + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + String projectName = configuration.getProjectName(model); + IProject project = configuration.getProject(ResourcesPlugin.getWorkspace().getRoot(), model); + + // XXX respect parent's setting for separate projects for modules + // XXX should run update sources on parent instead of creating new module project + + plugin.getProjectConfigurationManager().createSimpleProject(project, location.append(moduleName), model, + folders, configuration, monitor); + + setModule(projectName); + + return Status.OK_STATUS; + } + }; + + } else { + Model model = parametersPage.getModel(); + + final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(moduleName); + final Archetype archetype = archetypePage.getArchetype(); + + final String groupId = model.getGroupId(); + final String artifactId = model.getArtifactId(); + final String version = model.getVersion(); + final String javaPackage = parametersPage.getJavaPackage(); + final Properties properties = parametersPage.getProperties(); + + job = new WorkspaceJob(Messages.getString("wizard.project.job.creating", archetype.getArtifactId())) { //$NON-NLS-1$ + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + MavenPlugin plugin = MavenPlugin.getDefault(); + plugin.getProjectConfigurationManager().createArchetypeProject(project, location, archetype, // + groupId, artifactId, version, javaPackage, properties, configuration, monitor); + + setModule(moduleName); + + return Status.OK_STATUS; + } + }; + } + job.addJobChangeListener(new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + final IStatus result = event.getResult(); + if(result.isOK()) { + if(!isEditor) { + plugin.getMavenModelManager().addModule(parentPom, moduleName); + } + + } else { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + MessageDialog.openError(getShell(), // + Messages.getString("wizard.project.job.failed", moduleName), // //$NON-NLS-1$ + result.getMessage()); + } + }); + } + } + }); + job.setRule(plugin.getProjectConfigurationManager().getRule()); + job.schedule(); + + if(isEditor) { + try { + job.join(); + } catch(InterruptedException ex) { + // ignore + } + } + + return true; + } + + void setModule(String moduleName) { + this.moduleName = moduleName; + } + + public String getModuleName() { + return this.moduleName; + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizardParentPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizardParentPage.java new file mode 100644 index 00000000..4b2c1b14 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenModuleWizardParentPage.java @@ -0,0 +1,310 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Maven2ModuleParentPage + */ +public class MavenModuleWizardParentPage extends AbstractMavenWizardPage { + + /** the module name input field */ + private Combo moduleNameCombo; + + /** the parent project input field */ + protected Text parentProjectText; + + /** the "create simple project" checkbox */ + private Button simpleProject; + + /** the parent object entity */ + protected Object parentObject; + + /** the parent container */ + private IContainer parentContainer; + + /** the parent POM file */ + private IFile pom; + + /** the parent model */ + private Model parentModel; + + /** working set selector widget */ + private WorkingSetGroup workingSetGroup; + + + /** Creates a new page. */ + public MavenModuleWizardParentPage(ProjectImportConfiguration projectImportConfiguration) { + super("MavenModuleWizardParentPage", projectImportConfiguration); + setTitle(Messages.getString("wizard.module.page.parent.title")); //$NON-NLS-1$ + setDescription(Messages.getString("wizard.module.page.parent.description")); //$NON-NLS-1$ + setPageComplete(false); + } + + /** Creates the page controls. */ + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + container.setLayout(new GridLayout(3, false)); + + simpleProject = new Button(container, SWT.CHECK); + simpleProject.setText(Messages.getString("wizard.project.page.project.simpleProject")); //$NON-NLS-1$ + simpleProject.setData("name", "simpleProjectButton"); //$NON-NLS-1$ //$NON-NLS-2$ + simpleProject.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + simpleProject.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + validate(); + } + }); + + Label nameLabel = new Label(container, SWT.NONE); + GridData gd_nameLabel = new GridData(); + gd_nameLabel.verticalIndent = 10; + nameLabel.setLayoutData(gd_nameLabel); + nameLabel.setText(Messages.getString("wizard.module.page.parent.moduleName")); //$NON-NLS-1$ + + moduleNameCombo = new Combo(container, SWT.BORDER); + GridData gd_moduleNameCombo = new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1); + gd_moduleNameCombo.verticalIndent = 10; + moduleNameCombo.setLayoutData(gd_moduleNameCombo); + moduleNameCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }); + addFieldWithHistory("moduleName", moduleNameCombo); //$NON-NLS-1$ + + Label parentLabel = new Label(container, SWT.NONE); + parentLabel.setText(Messages.getString("wizard.module.page.parent.parentProject")); //$NON-NLS-1$ + + parentProjectText = new Text(container, SWT.BORDER); + parentProjectText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1)); + parentProjectText.setEditable(false); + + Button browseButton = new Button(container, SWT.NONE); + browseButton.setText(Messages.getString("wizard.module.page.parent.browse")); //$NON-NLS-1$ + browseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + MavenProjectSelectionDialog dialog = new MavenProjectSelectionDialog(getShell()); + if(dialog.open() == Window.OK) { + setParent(dialog.getFirstResult()); + validate(); + } + } + }); + + this.workingSetGroup = new WorkingSetGroup(container, getImportConfiguration(), getShell()); + + createAdvancedSettings(container, new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); + + initialize(); + + setControl(container); + } + + /** Initializes the GUI components and validates the page. */ + private void initialize() { + loadParent(); + validate(); + } + + /** Validates the data entered. */ + void validate() { + String moduleName = moduleNameCombo.getText().trim(); + if(moduleName.length() == 0) { + setErrorMessage(null); + setMessage(Messages.getString("wizard.module.page.parent.validator.moduleName")); //$NON-NLS-1$ + setPageComplete(false); + return; + } + + // check whether the project name is valid + IStatus nameStatus = + ResourcesPlugin.getWorkspace().validateName(moduleName, IResource.PROJECT); + if(!nameStatus.isOK()) { + setErrorMessage(nameStatus.getMessage()); + setPageComplete(false); + return; + } + + // check if the given folder already exists + if(parentContainer != null && parentContainer.exists(new Path(moduleName))) { + setErrorMessage(Messages.getString("wizard.module.page.parent.validator.nameExists")); //$NON-NLS-1$ + setPageComplete(false); + return; + } + + if(pom == null) { + setErrorMessage(null); + setMessage(Messages.getString("wizard.module.page.parent.validator.parentProject")); //$NON-NLS-1$ + setPageComplete(false); + return; + } + if(!validateParent()){ + return; + } + setErrorMessage(null); + setMessage(null); + setPageComplete(true); + } + + /** Assigns a parent object. */ + public void setParent(Object parent) { + parentObject = parent; + loadParent(); + } + + /** Loads the data from the parent object. */ + protected void loadParent() { + if(parentObject == null) { + return; + } + + int type = SelectionUtil.getElementType(parentObject); + + if(SelectionUtil.POM_FILE == type) { + pom = SelectionUtil.getType(parentObject, IFile.class); + } else if(SelectionUtil.PROJECT_WITH_NATURE == type) { + IProject project = SelectionUtil.getType(parentObject, IProject.class); + pom = project.getFile(IMavenConstants.POM_FILE_NAME); + + workingSetGroup.selectWorkingSets(SelectionUtil.getAssignedWorkingSets(project)); + } else if(parentObject instanceof IContainer) { + pom = ((IContainer) parentObject).getFile(new Path(IMavenConstants.POM_FILE_NAME)); + } + + if(pom != null && pom.exists()) { + parentObject = pom; + parentContainer = pom.getParent(); + + MavenPlugin plugin = MavenPlugin.getDefault(); + try { + parentModel = plugin.getMavenModelManager().readMavenModel(pom); + validateParent(); + parentProjectText.setText(parentModel.getArtifactId()); + } catch(CoreException e) { + MavenLogger.log("Error loading POM: " + e.getMessage(), e); + } + } + } + + private boolean validateParent(){ + if(parentModel != null){ + if(!"pom".equals(parentModel.getPackaging())){ //$NON-NLS-1$ + setMessage(null); + setErrorMessage(org.eclipse.m2e.core.internal.Messages.MavenModuleWizardParentPage_error); + setPageComplete(false); + return false; + } + } + return true; + } + + /** Returns "true" if the user chose not to use archetypes. */ + public boolean isSimpleProject() { + return simpleProject.getSelection(); + } + + /** Skips the archetype selection page if the user chooses a simple project. */ + public IWizardPage getNextPage() { + return getWizard().getPage( + isSimpleProject() ? "MavenProjectWizardArtifactPage" : "MavenProjectWizardArchetypePage"); + } + + /** Returns the module name. */ + public String getModuleName() { + return moduleNameCombo.getText(); + } + + /** Returns the parent model. */ + public Model getParentModel() { + return parentModel; + } + + /** Returns the parent POM file handle. */ + public IFile getPom() { + return pom; + } + + /** Returns the parent container. */ + public IContainer getParentContainer() { + return parentContainer; + } + + /** Offers a listener hookup to the pages watching the module name field. */ + public void addModuleNameListener(ModifyListener modifyListener) { + moduleNameCombo.addModifyListener(modifyListener); + } + + /** Unhooks the listener watching the module name field. */ + public void removesModuleNameListener(ModifyListener modifyListener) { + moduleNameCombo.removeModifyListener(modifyListener); + } + + /** Offers a listener hookup to the pages watching the parent name field. */ + public void addParentProjectListener(ModifyListener modifyListener) { + parentProjectText.addModifyListener(modifyListener); + } + + /** Unhooks the listener watching the parent name field. */ + public void removesParentProjectListener(ModifyListener modifyListener) { + parentProjectText.removeModifyListener(modifyListener); + } + + /** Offers a listener hookup to the pages watching the archetype switch. */ + public void addArchetypeSelectionListener(SelectionListener selectionListener) { + simpleProject.addSelectionListener(selectionListener); + } + + /** Removes the listener watching the project name field. */ + public void removeArchetypeSelectionListener(SelectionListener selectionListener) { + simpleProject.removeSelectionListener(selectionListener); + } + + /** Cleans up. */ + public void dispose() { + super.dispose(); + workingSetGroup.dispose(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenParentComponent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenParentComponent.java new file mode 100644 index 00000000..b40aaeac --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenParentComponent.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; + +import org.eclipse.m2e.core.core.Messages; + + +/** + * Wizard page component showing panel project properties. + */ +public class MavenParentComponent extends Composite { + + /** parent artifact id input field */ + private Combo parentArtifactIdCombo; + + /** parent group id input field */ + private Combo parentGroupIdCombo; + + /** parent version input field */ + private Combo parentVersionCombo; + + /** the "clear parent section" button */ + private Button parentClearButton; + + /** the "browse..." button */ + private Button parentBrowseButton; + + private Label groupIdLabel; + + private Label artifactIdLabel; + + private Label versionLabel; + + /** Creates a new panel with parent controls. */ + public MavenParentComponent(Composite parent, int style) { + super(parent, SWT.NONE); + + boolean readonly = (style & SWT.READ_ONLY) != 0; + + GridLayout topLayout = new GridLayout(); + topLayout.marginHeight = 0; + topLayout.marginWidth = 0; + setLayout(topLayout); + + Group group = new Group(this, SWT.NONE); + group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); + group.setText(Messages.getString("wizard.project.page.artifact.parent.title")); //$NON-NLS-1$ + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 3; + group.setLayout(gridLayout); + + groupIdLabel = new Label(group, SWT.NONE); + groupIdLabel.setText(Messages.getString("wizard.project.page.artifact.parent.groupId")); //$NON-NLS-1$ + + parentGroupIdCombo = new Combo(group, SWT.NONE); + parentGroupIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + parentGroupIdCombo.setData("name", "parentGroupIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + parentGroupIdCombo.setEnabled(!readonly); + + artifactIdLabel = new Label(group, SWT.NONE); + artifactIdLabel.setText(Messages.getString("wizard.project.page.artifact.parent.artifactId")); //$NON-NLS-1$ + + parentArtifactIdCombo = new Combo(group, SWT.NONE); + parentArtifactIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + parentArtifactIdCombo.setData("name", "parentArtifactIdCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + parentArtifactIdCombo.setEnabled(!readonly); + + versionLabel = new Label(group, SWT.NONE); + versionLabel.setText(Messages.getString("wizard.project.page.artifact.parent.version")); //$NON-NLS-1$ + + parentVersionCombo = new Combo(group, SWT.NONE); + GridData gd_versionCombo = new GridData(SWT.LEFT, SWT.CENTER, true, false); + gd_versionCombo.widthHint = 150; + parentVersionCombo.setLayoutData(gd_versionCombo); + parentVersionCombo.setEnabled(!readonly); + parentVersionCombo.setData("name", "parentVersionCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + + if(!readonly) { + Composite buttonPanel = new Composite(group, SWT.NONE); + RowLayout rowLayout = new RowLayout(); + rowLayout.pack = false; + rowLayout.marginTop = 0; + rowLayout.marginRight = 0; + rowLayout.marginLeft = 0; + rowLayout.marginBottom = 0; + buttonPanel.setLayout(rowLayout); + buttonPanel.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false)); + + parentBrowseButton = new Button(buttonPanel, SWT.NONE); + parentBrowseButton.setText(Messages.getString("wizard.project.page.artifact.parent.browse")); //$NON-NLS-1$ + parentBrowseButton.setData("name", "parentBrowseButton"); //$NON-NLS-1$ //$NON-NLS-2$ + + parentClearButton = new Button(buttonPanel, SWT.NONE); + parentClearButton.setText(Messages.getString("wizard.project.page.artifact.parent.clear")); //$NON-NLS-1$ + parentClearButton.setData("name", "parentClearButton"); //$NON-NLS-1$ //$NON-NLS-2$ + parentClearButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setValues("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + }); + } + } + + public Combo getGroupIdCombo() { + return parentGroupIdCombo; + } + + public Combo getArtifactIdCombo() { + return this.parentArtifactIdCombo; + } + + public Combo getVersionCombo() { + return this.parentVersionCombo; + } + + public void setWidthGroup(WidthGroup widthGroup) { + widthGroup.addControl(this.groupIdLabel); + widthGroup.addControl(this.artifactIdLabel); + widthGroup.addControl(this.versionLabel); + } + + /** Adds modify listener to the input controls. */ + public void addModifyListener(ModifyListener listener) { + parentArtifactIdCombo.addModifyListener(listener); + parentGroupIdCombo.addModifyListener(listener); + parentVersionCombo.addModifyListener(listener); + } + + /** Removes the listener from the input controls. */ + public void removeModifyListener(ModifyListener listener) { + parentArtifactIdCombo.removeModifyListener(listener); + parentGroupIdCombo.removeModifyListener(listener); + parentVersionCombo.removeModifyListener(listener); + } + + /** Adds selection listener to the "browse" button. */ + public void addBrowseButtonListener(SelectionListener listener) { + if(parentBrowseButton != null) { + parentBrowseButton.addSelectionListener(listener); + } + } + + /** Removes the selection listener from the "browse" button. */ + public void removeBrowseButtonListener(SelectionListener listener) { + if(parentBrowseButton != null) { + parentBrowseButton.removeSelectionListener(listener); + } + } + + /** Enables the "clear" button. */ + public void setClearButtonEnabled(boolean enabled) { + if(parentClearButton != null) { + parentClearButton.setEnabled(enabled); + } + } + + /** Sets the parent group values. */ + public void setValues(String groupId, String artifactId, String version) { + parentGroupIdCombo.setText(groupId==null ? "" : groupId); //$NON-NLS-1$ + parentArtifactIdCombo.setText(artifactId==null ? "" : artifactId); //$NON-NLS-1$ + parentVersionCombo.setText(version==null ? "" : version); //$NON-NLS-1$ + } + + /** Updates a Maven model. */ + public void updateModel(Model model) { + String groupId = parentGroupIdCombo.getText().trim(); + if(groupId.length() > 0) { + Parent parent = new Parent(); + parent.setGroupId(groupId); + parent.setArtifactId(parentArtifactIdCombo.getText().trim()); + parent.setVersion(parentVersionCombo.getText().trim()); + model.setParent(parent); + } + } + + /** + * Validates the inputs to make sure all three fields are present in the same time, or none at all. + */ + public boolean validate() { + int parentCheck = 0; + if(parentGroupIdCombo.getText().trim().length() > 0) { + parentCheck++ ; + } + if(parentArtifactIdCombo.getText().trim().length() > 0) { + parentCheck++ ; + } + if(parentVersionCombo.getText().trim().length() > 0) { + parentCheck++ ; + } + + setClearButtonEnabled(parentCheck > 0); + + return parentCheck == 0 || parentCheck == 3; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomSelectionComponent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomSelectionComponent.java new file mode 100644 index 00000000..6c708de5 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomSelectionComponent.java @@ -0,0 +1,580 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.ibm.icu.text.DateFormat; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IDecoration; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +import org.apache.lucene.search.BooleanQuery; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.index.IndexedArtifact; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.index.UserInputSearchExpression; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * MavenPomSelectionComposite + * + * @author Eugene Kuleshov + */ +public class MavenPomSelectionComponent extends Composite { + + /* (non-Javadoc) + * @see org.eclipse.swt.widgets.Widget#dispose() + */ + public void dispose() { + if(searchJob != null) { + searchJob.cancel(); + } + super.dispose(); + } + + Text searchText = null; + + TreeViewer searchResultViewer = null; + + Button javadocCheckBox; + + Button sourcesCheckBox; + + Button testCheckBox; + + /** + * One of {@link IIndex#SEARCH_ARTIFACT}, {@link IIndex#SEARCH_CLASS_NAME}, + */ + String queryType; + + SearchJob searchJob; + + private IStatus status; + + private ISelectionChangedListener selectionListener; + + public static final String P_SEARCH_INCLUDE_JAVADOC = "searchIncludesJavadoc"; //$NON-NLS-1$ + + public static final String P_SEARCH_INCLUDE_SOURCES = "searchIncludesSources"; //$NON-NLS-1$ + + public static final String P_SEARCH_INCLUDE_TESTS = "searchIncludesTests"; //$NON-NLS-1$ + + private static final long SHORT_DELAY = 150L; + + private static final long LONG_DELAY = 500L; + + final HashSet<String> artifactKeys = new HashSet<String>(); + final HashSet<String> managedKeys = new HashSet<String>(); + + public MavenPomSelectionComponent(Composite parent, int style) { + super(parent, style); + createSearchComposite(); + } + + private void createSearchComposite() { + GridLayout gridLayout = new GridLayout(2, false); + gridLayout.marginWidth = 0; + gridLayout.marginHeight = 0; + setLayout(gridLayout); + + Label searchTextlabel = new Label(this, SWT.NONE); + searchTextlabel.setText(Messages.MavenPomSelectionComponent_search_title); + searchTextlabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + searchText = new Text(this, SWT.BORDER | SWT.SEARCH); + searchText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); + searchText.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if(e.keyCode == SWT.ARROW_DOWN) { + searchResultViewer.getTree().setFocus(); + selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade(); + } + } + }); + + searchText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + scheduleSearch(searchText.getText(), true); + } + }); + + Label searchResultsLabel = new Label(this, SWT.NONE); + searchResultsLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); + searchResultsLabel.setText(Messages.MavenPomSelectionComponent_lblResults); + + Tree tree = new Tree(this, SWT.BORDER | SWT.SINGLE); + tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + tree.setData("name", "searchResultTree"); //$NON-NLS-1$ //$NON-NLS-2$ + tree.addFocusListener(new FocusListener() { + + public void focusGained(FocusEvent e) { + selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade(); + } + + public void focusLost(FocusEvent e) { + + } + }); + + searchResultViewer = new TreeViewer(tree); + } + + void selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade() { + // + // If we have started a new search when focus is passed to the tree viewer we will automatically select + // the first element if no element has been selected from a previous expedition into the tree viewer. + // + if(searchResultViewer.getTree().getItemCount() > 0 && searchResultViewer.getSelection().isEmpty()) { + Object artifact = searchResultViewer.getTree().getTopItem().getData(); + searchResultViewer.setSelection(new StructuredSelection(artifact), true); + } + } + + protected boolean showClassifiers() { + return (queryType != null && IIndex.SEARCH_ARTIFACT.equals(queryType)); + } + + private void setupButton(final Button button, String label, final String prefName, int horizontalIndent) { + button.setText(label); + GridData gd = new GridData(SWT.LEFT, SWT.TOP, false, false); + gd.horizontalIndent = horizontalIndent; + button.setLayoutData(gd); + boolean check = MavenPlugin.getDefault().getPreferenceStore().getBoolean(prefName); + button.setSelection(check); + button.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + boolean checked = button.getSelection(); + MavenPlugin.getDefault().getPreferenceStore().setValue(prefName, checked); + scheduleSearch(searchText.getText(), false); + } + }); + } + + public void init(String queryText, String queryType, Set<ArtifactKey> artifacts, Set<ArtifactKey> managed) { + this.queryType = queryType; + + if(queryText != null) { + searchText.setText(queryText); + } + + if(artifacts != null) { + for(ArtifactKey a : artifacts) { + artifactKeys.add(a.getGroupId() + ":" + a.getArtifactId()); //$NON-NLS-1$ + artifactKeys.add(a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (managed != null) { + for (ArtifactKey a : managed) { + managedKeys.add(a.getGroupId() + ":" + a.getArtifactId()); //$NON-NLS-1$ + managedKeys.add(a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + searchResultViewer.setContentProvider(new SearchResultContentProvider()); + searchResultViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new SearchResultLabelProvider(artifactKeys, managedKeys, queryType))); + searchResultViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if(!selection.isEmpty()) { + if(selection.size() == 1) { + IndexedArtifactFile f = getSelectedIndexedArtifactFile(selection.getFirstElement()); + // int severity = artifactKeys.contains(f.group + ":" + f.artifact) ? IStatus.ERROR : IStatus.OK; + int severity = IStatus.OK; + String date = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(f.date); + setStatus( + severity, + NLS.bind(Messages.MavenPomSelectionComponent_detail1, f.fname, + (f.size != -1 ? NLS.bind(Messages.MavenPomSelectionComponent_details2, date, f.size) : date))); + } else { + setStatus(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_selected, selection.size())); + } + } else { + setStatus(IStatus.ERROR, Messages.MavenPomSelectionComponent_nosel); + } + } + }); + setupClassifiers(); + setStatus(IStatus.ERROR, ""); //$NON-NLS-1$ + scheduleSearch(queryText, false); + } + + protected void setupClassifiers() { + if(showClassifiers()) { + Composite includesComp = new Composite(this, SWT.NONE); + includesComp.setLayout(new GridLayout(3, true)); + GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, false); + includesComp.setLayoutData(gd); + + javadocCheckBox = new Button(includesComp, SWT.CHECK); + setupButton(javadocCheckBox, Messages.MavenPomSelectionComponent_btnJavadoc, P_SEARCH_INCLUDE_JAVADOC, 0); + + sourcesCheckBox = new Button(includesComp, SWT.CHECK); + setupButton(sourcesCheckBox, Messages.MavenPomSelectionComponent_btnSource, P_SEARCH_INCLUDE_SOURCES, 10); + + testCheckBox = new Button(includesComp, SWT.CHECK); + setupButton(testCheckBox, Messages.MavenPomSelectionComponent_btnTests, P_SEARCH_INCLUDE_TESTS, 10); + } + } + + public IStatus getStatus() { + return this.status; + } + + public void addDoubleClickListener(IDoubleClickListener listener) { + searchResultViewer.addDoubleClickListener(listener); + } + + public void addSelectionChangedListener(ISelectionChangedListener listener) { + this.selectionListener = listener; + } + + void setStatus(int severity, String message) { + this.status = new Status(severity, IMavenConstants.PLUGIN_ID, 0, message, null); + if(selectionListener != null) { + selectionListener.selectionChanged(new SelectionChangedEvent(searchResultViewer, searchResultViewer + .getSelection())); + } + } + + public IndexedArtifact getIndexedArtifact() { + IStructuredSelection selection = (IStructuredSelection) searchResultViewer.getSelection(); + Object element = selection.getFirstElement(); + if(element instanceof IndexedArtifact) { + return (IndexedArtifact) element; + } + TreeItem[] treeItems = searchResultViewer.getTree().getSelection(); + if(treeItems.length == 0) { + return null; + } + return (IndexedArtifact) treeItems[0].getParentItem().getData(); + } + + public IndexedArtifactFile getIndexedArtifactFile() { + IStructuredSelection selection = (IStructuredSelection) searchResultViewer.getSelection(); + return getSelectedIndexedArtifactFile(selection.getFirstElement()); + } + + IndexedArtifactFile getSelectedIndexedArtifactFile(Object element) { + if(element instanceof IndexedArtifact) { + //the idea here is that if we have a managed version for something, then the IndexedArtifact shall + //represent that value.. + IndexedArtifact ia = (IndexedArtifact)element; + if (managedKeys.contains(getKey(ia))) { + for (IndexedArtifactFile file : ia.getFiles()) { + if (managedKeys.contains(getKey(file))) { + return file; + } + } + } + return ia.getFiles().iterator().next(); + } + return (IndexedArtifactFile) element; + } + + void scheduleSearch(String query, boolean delay) { + if(query != null && query.length() > 2) { + if(searchJob == null) { + IndexManager indexManager = MavenPlugin.getDefault().getIndexManager(); + searchJob = new SearchJob(queryType, indexManager); + } else { + if(!searchJob.cancel()) { + //for already running ones, just create new instance so that the previous one can piecefully die + //without preventing the new one from completing first + IndexManager indexManager = MavenPlugin.getDefault().getIndexManager(); + searchJob = new SearchJob(queryType, indexManager); + } + } + searchJob.setQuery(query.toLowerCase()); + searchJob.schedule(delay ? LONG_DELAY : SHORT_DELAY); + } else { + if(searchJob != null) { + searchJob.cancel(); + } + } + } + + static String getKey(IndexedArtifactFile file) { + return file.group + ":" + file.artifact + ":" + file.version; //$NON-NLS-1$ //$NON-NLS-2$ + } + static String getKey(IndexedArtifact art) { + return art.getGroupId() + ":" + art.getArtifactId(); //$NON-NLS-1$ + } + + + /** + * Search Job + */ + private class SearchJob extends Job { + + private IndexManager indexManager; + + private String query; + + private String field; + + private volatile boolean stop = false; + + public SearchJob(String field, IndexManager indexManager) { + super(Messages.MavenPomSelectionComponent_searchJob); + this.field = field; + this.indexManager = indexManager; + } + + public void setQuery(String query) { + this.query = query; + } + + public boolean shouldRun() { + stop = false; + return super.shouldRun(); + } + + public int getClassifier() { + int classifier = IIndex.SEARCH_JARS; + if(MavenPlugin.getDefault().getPreferenceStore().getBoolean(P_SEARCH_INCLUDE_JAVADOC)) { + classifier = classifier + IIndex.SEARCH_JAVADOCS; + } + if(MavenPlugin.getDefault().getPreferenceStore().getBoolean(P_SEARCH_INCLUDE_SOURCES)) { + classifier = classifier + IIndex.SEARCH_SOURCES; + } + if(MavenPlugin.getDefault().getPreferenceStore().getBoolean(P_SEARCH_INCLUDE_TESTS)) { + classifier = classifier + IIndex.SEARCH_TESTS; + } + return classifier; + } + + protected IStatus run(IProgressMonitor monitor) { + int classifier = showClassifiers() ? getClassifier() : IIndex.SEARCH_ALL; + if(searchResultViewer == null || searchResultViewer.getControl() == null + || searchResultViewer.getControl().isDisposed()) { + return Status.CANCEL_STATUS; + } + if(query != null) { + String activeQuery = query; + try { + setResult(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_searching, activeQuery.toLowerCase()), + null); + + // TODO: cstamas identified this as "user input", true? + Map<String, IndexedArtifact> res = indexManager.getAllIndexes().search( new UserInputSearchExpression(activeQuery), field, classifier); + setResult(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_results, activeQuery, res.size()), res); + } catch(BooleanQuery.TooManyClauses ex) { + setResult(IStatus.ERROR, Messages.MavenPomSelectionComponent_toomany, + Collections.<String, IndexedArtifact> emptyMap()); + } catch(final RuntimeException ex) { + setResult(IStatus.ERROR, NLS.bind(Messages.MavenPomSelectionComponent_error, ex.toString()), + Collections.<String, IndexedArtifact> emptyMap()); + } catch(final Exception ex) { + setResult(IStatus.ERROR, NLS.bind(Messages.MavenPomSelectionComponent_error, ex.getMessage()), + Collections.<String, IndexedArtifact> emptyMap()); + } + } + return Status.OK_STATUS; + } + + protected void canceling() { + stop = true; + } + + private void setResult(final int severity, final String message, final Map<String, IndexedArtifact> result) { + if(stop) + return; + Display.getDefault().syncExec(new Runnable() { + public void run() { + setStatus(severity, message); + if(result != null) { + if(!searchResultViewer.getControl().isDisposed()) { + searchResultViewer.setInput(result); + } + } + } + }); + } + } + + public static class SearchResultLabelProvider extends LabelProvider implements IColorProvider, DelegatingStyledCellLabelProvider.IStyledLabelProvider { + private final Set<String> artifactKeys; + + private final String queryType; + private final Set<String> managedKeys; + + /** + * both managedkeys and artifctkeys are supposed to hold both gr:art:ver combos and gr:art combos + * @param artifactKeys + * @param managedKeys + * @param queryType + */ + public SearchResultLabelProvider(Set<String> artifactKeys, Set<String> managedKeys, String queryType) { + this.artifactKeys = artifactKeys; + this.queryType = queryType; + this.managedKeys = managedKeys; + } + + public String getText(Object element) { + return super.getText(element); + } + + protected String getRepoDisplayName(String repo) { + return repo; + } + + public Color getForeground(Object element) { + if(element instanceof IndexedArtifactFile) { + IndexedArtifactFile f = (IndexedArtifactFile) element; + if(artifactKeys.contains(getKey(f))) { + return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); + } + } else if(element instanceof IndexedArtifact) { + IndexedArtifact i = (IndexedArtifact) element; + if(artifactKeys.contains(getKey(i))) { + return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); + } + } + return null; + } + + public Color getBackground(Object element) { + return null; + } + + public Image getImage(Object element) { + if(element instanceof IndexedArtifactFile) { + IndexedArtifactFile f = (IndexedArtifactFile) element; + if (managedKeys.contains(getKey(f))) { + return MavenImages.getOverlayImage(f.sourcesExists==IIndex.PRESENT ? MavenImages.PATH_VERSION_SRC : MavenImages.PATH_VERSION, + MavenImages.PATH_LOCK, IDecoration.BOTTOM_LEFT); + } + + if(f.sourcesExists==IIndex.PRESENT) { + return MavenImages.IMG_VERSION_SRC; + } + return MavenImages.IMG_VERSION; + } else if(element instanceof IndexedArtifact) { + IndexedArtifact i = (IndexedArtifact) element; + if (managedKeys.contains(getKey(i))) { + return MavenImages.getOverlayImage(MavenImages.PATH_JAR, MavenImages.PATH_LOCK, IDecoration.BOTTOM_LEFT); + } + return MavenImages.IMG_JAR; + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider#getStyledText(java.lang.Object) + */ + public StyledString getStyledText(Object element) { + if(element instanceof IndexedArtifact) { + IndexedArtifact a = (IndexedArtifact) element; + String name = (a.getClassname() == null ? "" : a.getClassname() + " " + a.getPackageName() + " ") + a.getGroupId() + " " + a.getArtifactId(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + StyledString ss = new StyledString(); + ss.append(name); + if (managedKeys.contains(getKey(a))) { + ss.append(Messages.MavenPomSelectionComponent_managed_decoration, StyledString.DECORATIONS_STYLER); + } + return ss; + } else if(element instanceof IndexedArtifactFile) { + IndexedArtifactFile f = (IndexedArtifactFile) element; + StyledString ss = new StyledString(); + String name = f.version + " [" + (f.type == null ? "jar" : f.type) + (f.classifier != null ? ", " + f.classifier : "") + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + ss.append(name); + if (managedKeys.contains(getKey(f))) { + ss.append(Messages.MavenPomSelectionComponent_managed_decoration, StyledString.DECORATIONS_STYLER); + } + return ss; + } + return new StyledString(); + } + + } + + public static class SearchResultContentProvider implements ITreeContentProvider { + private static Object[] EMPTY = new Object[0]; + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getElements(Object inputElement) { + if(inputElement instanceof Map) { + return ((Map<?, ?>) inputElement).values().toArray(); + } + return EMPTY; + } + + public Object[] getChildren(Object parentElement) { + if(parentElement instanceof IndexedArtifact) { + IndexedArtifact a = (IndexedArtifact) parentElement; + return a.getFiles().toArray(); + } + return EMPTY; + } + + public boolean hasChildren(Object element) { + return element instanceof IndexedArtifact; + } + + public Object getParent(Object element) { + return null; + } + + public void dispose() { + + } + + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizard.java new file mode 100644 index 00000000..b3c5b2cc --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizard.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWizard; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * New POM wizard + */ +public class MavenPomWizard extends Wizard implements INewWizard { + private MavenPomWizardPage artifactPage; + + private MavenDependenciesWizardPage dependenciesPage; + + private ISelection selection; + + /** + * Constructor for MavenPomWizard. + */ + public MavenPomWizard() { + super(); + setNeedsProgressMonitor(true); + setWindowTitle(Messages.MavenPomWizard_title); + } + + /** + * Adding the page to the wizard. + */ + + public void addPages() { + artifactPage = new MavenPomWizardPage(selection); + dependenciesPage = new MavenDependenciesWizardPage(); + dependenciesPage.setDependencies(new Dependency[0]); + + addPage(artifactPage); + addPage(dependenciesPage); + } + + /** + * This method is called when 'Finish' button is pressed in the wizard. We will create an operation and run it using + * wizard as execution context. + */ + public boolean performFinish() { + final String projectName = artifactPage.getProject(); + final Model model = artifactPage.getModel(); + @SuppressWarnings("unchecked") + List<Dependency> modelDependencies = model.getDependencies(); + modelDependencies.addAll(Arrays.asList(dependenciesPage.getDependencies())); + + IRunnableWithProgress op = new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InvocationTargetException { + monitor.beginTask(Messages.MavenPomWizard_task, 1); + try { + doFinish(projectName, model, monitor); + monitor.worked(1); + } catch(CoreException e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + }; + + try { + getContainer().run(true, false, op); + } catch(InterruptedException e) { + return false; + } catch(InvocationTargetException e) { + Throwable realException = e.getTargetException(); + MessageDialog.openError(getShell(), Messages.MavenPomWizard_error_title, realException.getMessage()); + return false; + } + return true; + } + + /** + * The worker method. It will find the container, create the file if missing or just replace its contents, and open + * the editor on the newly created file. + */ + void doFinish(String projectName, final Model model, IProgressMonitor monitor) throws CoreException { + // monitor.beginTask("Creating " + fileName, 2); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IResource resource = root.findMember(new Path(projectName)); + if(!resource.exists() || (resource.getType() & IResource.FOLDER | IResource.PROJECT) == 0) { + // TODO show warning popup + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, + NLS.bind(Messages.MavenPomWizard_status_not_exists, projectName), null)); + } + + IContainer container = (IContainer) resource; + final IFile file = container.getFile(new Path(IMavenConstants.POM_FILE_NAME)); + if(file.exists()) { + // TODO show warning popup + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenPomWizard_error_exists, null)); + } + + final File pom = file.getLocation().toFile(); + + try { + MavenModelManager modelManager = MavenPlugin.getDefault().getMavenModelManager(); + modelManager.createMavenModel(file, model); + + getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + try { + IDE.openEditor(page, file, true); + } catch(PartInitException e) { + } + } + }); + + } catch(Exception ex) { + MavenLogger.log("Unable to create POM " + pom + "; " + ex.getMessage(), ex); + + } + } + + /** + * We will accept the selection in the workbench to see if we can initialize from it. + * + * @see IWorkbenchWizard#init(IWorkbench, IStructuredSelection) + */ + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizardPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizardPage.java new file mode 100644 index 00000000..b2b62645 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenPomWizardPage.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.ContainerSelectionDialog; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.internal.Messages; + + +/** + * Wizard page used to specify basic POM parameters + */ +public class MavenPomWizardPage extends AbstractMavenWizardPage { + private Text projectText; + + private ISelection selection; + + private MavenArtifactComponent pomComponent; + + public MavenPomWizardPage(ISelection selection) { + super("wizardPage"); //$NON-NLS-1$ + setTitle(Messages.MavenPomWizardPage_title); + setDescription(Messages.MavenPomWizardPage_desc); + this.selection = selection; + } + + public void createControl(Composite parent) { + GridLayout layout = new GridLayout(); + layout.numColumns = 3; + layout.makeColumnsEqualWidth = false; + + Composite container = new Composite(parent, SWT.NULL); + container.setLayout(layout); + + ModifyListener modifyingListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + dialogChanged(); + } + }; + + Label label = new Label(container, SWT.NULL); + label.setText(Messages.MavenPomWizardPage_lblProject); + + projectText = new Text(container, SWT.BORDER | SWT.SINGLE); + projectText.setEditable(false); + projectText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + projectText.addModifyListener(modifyingListener); + + Button button = new Button(container, SWT.PUSH); + final GridData gridData_2 = new GridData(); + button.setLayoutData(gridData_2); + button.setText(Messages.MavenPomWizardPage_btnBrowse); + button.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleBrowse(); + } + }); + + pomComponent = new MavenArtifactComponent(container, SWT.NONE); + pomComponent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1)); + pomComponent.setModifyingListener(modifyingListener); + addFieldWithHistory("groupId", pomComponent.getGroupIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("artifactId", pomComponent.getArtifactIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("version", pomComponent.getVersionCombo()); //$NON-NLS-1$ + addFieldWithHistory("name", pomComponent.getNameCombo()); //$NON-NLS-1$ + + initialize(); + dialogChanged(); + setControl(container); + } + + /** + * Tests if the current workbench selection is a suitable container to use. + */ + private void initialize() { + if(selection != null && !selection.isEmpty() && selection instanceof IStructuredSelection) { + IStructuredSelection ssel = (IStructuredSelection) selection; + if(ssel.size() > 1) { + return; + } + Object obj = ssel.getFirstElement(); + if(obj instanceof IResource) { + IContainer container; + if(obj instanceof IContainer) { + container = (IContainer) obj; + } else { + container = ((IResource) obj).getParent(); + } + projectText.setText(container.getFullPath().toString()); + pomComponent.setArtifactId(container.getName()); + pomComponent.setGroupId(container.getName()); + } + } + + pomComponent.setVersion(MavenArtifactComponent.DEFAULT_VERSION); + pomComponent.setPackaging(MavenArtifactComponent.DEFAULT_PACKAGING); + } + + /** + * Uses the standard container selection dialog to choose the new value for the container field. + */ + void handleBrowse() { + ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(), + ResourcesPlugin.getWorkspace().getRoot(), false, Messages.MavenPomWizardPage_dialog_title); + dialog.showClosedProjects(false); + if(dialog.open() == Window.OK) { + Object[] result = dialog.getResult(); + if(result.length == 1) { + projectText.setText(((Path) result[0]).toString()); + } + } + +// IJavaModel javaModel = JavaCore.create(); +// +// IJavaProject[] projects; +// try { +// projects = javaModel.getJavaProjects(); +// } catch(JavaModelException e) { +// MavenLogger.log(e); +// projects = new IJavaProject[0]; +// } +// +// ILabelProvider labelProvider = new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT); +// ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), labelProvider); +// dialog.setTitle("Select Project"); +// dialog.setMessage("Choose project where POM will be created"); +// dialog.setElements(projects); +// +// String projectName = getProject(); +// if(projectName != null && projectName.length() > 0) { +// IJavaProject javaProject = javaModel.getJavaProject(projectName); +// if(javaProject != null) { +// dialog.setInitialSelections(new Object[] {javaProject}); +// } +// } +// +// if(dialog.open() == Window.OK) { +// projectText.setText(((IJavaProject) dialog.getFirstResult()).getProject().getFullPath().toString()); +// } + } + + /** + * Ensures that both text fields are set. + */ + void dialogChanged() { + IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(getProject())); + + if(getProject().length() == 0) { + updateStatus(Messages.MavenPomWizardPage_error_folder); + return; + } + if(container == null || (container.getType() & IResource.FOLDER | IResource.PROJECT) == 0) { + updateStatus(Messages.MavenPomWizardPage_error_folder2); + return; + } + if(!container.isAccessible()) { + updateStatus(Messages.MavenPomWizardPage_error_folder_write); + return; + } + + // TODO + if(pomComponent.getGroupId().length() == 0) { + updateStatus(Messages.MavenPomWizardPage_error_grid); + } + + if(pomComponent.getArtifactId().length() == 0) { + updateStatus(Messages.MavenPomWizardPage_error_artid); + } + + if(pomComponent.getVersion().length() == 0) { + updateStatus(Messages.MavenPomWizardPage_error_version); + } + + if(pomComponent.getPackaging().length() == 0) { + updateStatus(Messages.MavenPomWizardPage_error_pack); + } + + updateStatus(null); + } + + private void updateStatus(String message) { + setErrorMessage(message); + setPageComplete(message == null); + } + + public String getProject() { + return projectText.getText(); + } + + public Model getModel() { + return pomComponent.getModel(); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectCheckoutJob.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectCheckoutJob.java new file mode 100644 index 00000000..f894e0f6 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectCheckoutJob.java @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.IWizard; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.NewProjectAction; +import org.eclipse.ui.progress.IProgressConstants; + +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.LocalProjectScanner; +import org.eclipse.m2e.core.project.MavenProjectInfo; +import org.eclipse.m2e.core.project.MavenProjectScmInfo; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.scm.MavenCheckoutOperation; + + +/** + * Maven project checkout Job + * + * @author Eugene Kuleshov + */ +public abstract class MavenProjectCheckoutJob extends WorkspaceJob { + + final ProjectImportConfiguration configuration; + + boolean checkoutAllProjects; + + Collection<MavenProjectInfo> projects; + + File location; + + List<String> collectedLocations = new ArrayList<String>(); + + public MavenProjectCheckoutJob(ProjectImportConfiguration importConfiguration, boolean checkoutAllProjects) { + super(Messages.MavenProjectCheckoutJob_title); + this.configuration = importConfiguration; + this.checkoutAllProjects = checkoutAllProjects; + + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + addJobChangeListener(new CheckoutJobChangeListener()); + } + + public void setLocation(File location) { + this.location = location; + } + + protected abstract Collection<MavenProjectScmInfo> getProjects(IProgressMonitor monitor) throws InterruptedException; + + + // WorkspaceJob + + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + try { + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenConsole console = plugin.getConsole(); + + MavenCheckoutOperation operation = new MavenCheckoutOperation(location, getProjects(monitor), console); + operation.run(monitor); + collectedLocations.addAll(operation.getLocations()); + + IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); + + MavenModelManager modelManager = plugin.getMavenModelManager(); + + LocalProjectScanner scanner = new LocalProjectScanner(workspace.getLocation().toFile(), operation.getLocations(), + true, modelManager, console); + scanner.run(monitor); + + this.projects = plugin.getProjectConfigurationManager().collectProjects(scanner.getProjects()); + + if(checkoutAllProjects) { + // check if there any project name conflicts + for(MavenProjectInfo projectInfo : projects) { + Model model = projectInfo.getModel(); + if(model == null) { + model = modelManager.readMavenModel(projectInfo.getPomFile()); + projectInfo.setModel(model); + } + + String projectName = configuration.getProjectName(model); + IProject project = workspace.getProject(projectName); + if(project.exists()) { + checkoutAllProjects = false; + break; + } + } + } + + return Status.OK_STATUS; + + } catch(InterruptedException ex) { + return Status.CANCEL_STATUS; + } + } + + /** + * Checkout job listener + */ + final class CheckoutJobChangeListener extends JobChangeAdapter { + + public void done(IJobChangeEvent event) { + IStatus result = event.getResult(); + if(result.getSeverity() == IStatus.CANCEL) { + return; + } else if(!result.isOK()) { + // XXX report errors + return; + } + + if(projects.isEmpty()) { + MavenPlugin.getDefault().getConsole().logMessage("No Maven projects to import"); + + if(collectedLocations.size()==1) { + final String location = collectedLocations.get(0); + + DirectoryScanner projectScanner = new DirectoryScanner(); + projectScanner.setBasedir(location); + projectScanner.setIncludes(new String[] {"**/.project"}); //$NON-NLS-1$ + projectScanner.scan(); + + String[] projectFiles = projectScanner.getIncludedFiles(); + if(projectFiles!=null && projectFiles.length>0) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + boolean res = MessageDialog.openConfirm(Display.getDefault().getActiveShell(), // + Messages.MavenProjectCheckoutJob_confirm_title, // + Messages.MavenProjectCheckoutJob_confirm_message); + if(res) { + IWizard wizard = new ProjectsImportWizard(collectedLocations.get(0)); + WizardDialog dialog = new WizardDialog(Display.getDefault().getActiveShell(), wizard); + dialog.open(); + } else { + cleanup(collectedLocations); + } + } + }); + return; + } + + Display.getDefault().syncExec(new Runnable() { + public void run() { + boolean res = MessageDialog.openConfirm(Display.getDefault().getActiveShell(), // + Messages.MavenProjectCheckoutJob_confirm2_title, // + Messages.MavenProjectCheckoutJob_confirm2_message); + if(res) { + Clipboard clipboard = new Clipboard(Display.getDefault()); + clipboard.setContents(new Object[] { location }, new Transfer[] { TextTransfer.getInstance() }); + + NewProjectAction newProjectAction = new NewProjectAction(PlatformUI.getWorkbench().getActiveWorkbenchWindow()); + newProjectAction.run(); + } else { + cleanup(collectedLocations); + } + } + }); + return; + } + + cleanup(collectedLocations); + } + + if(checkoutAllProjects) { + final MavenPlugin plugin = MavenPlugin.getDefault(); + WorkspaceJob job = new WorkspaceJob(Messages.MavenProjectCheckoutJob_job) { + public IStatus runInWorkspace(IProgressMonitor monitor) { + Set<MavenProjectInfo> projectSet = plugin.getProjectConfigurationManager().collectProjects(projects); + + try { + plugin.getProjectConfigurationManager().importProjects(projectSet, configuration, monitor); + } catch(CoreException ex) { + plugin.getConsole().logError("Projects imported with errors"); + return ex.getStatus(); + } + + return Status.OK_STATUS; + } + }; + ISchedulingRule rule = ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(ResourcesPlugin.getWorkspace().getRoot()); + job.setRule(rule); + job.schedule(); + + } else { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + MavenImportWizard wizard = new MavenImportWizard(configuration, collectedLocations); + WizardDialog dialog = new WizardDialog(Display.getDefault().getActiveShell(), wizard); + int res = dialog.open(); + if(res == Window.CANCEL) { + cleanup(collectedLocations); + } + } + }); + } + } + + protected void cleanup(List<String> locations) { + MavenConsole console = MavenPlugin.getDefault().getConsole(); + for(String location : locations) { + try { + FileUtils.deleteDirectory(location); + } catch(IOException ex) { + String msg = "Can't delete " + location; + console.logError(msg + "; " + (ex.getMessage()==null ? ex.toString() : ex.getMessage())); //$NON-NLS-1$ + MavenLogger.log(msg, ex); + } + } + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectSelectionDialog.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectSelectionDialog.java new file mode 100644 index 00000000..77db0415 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectSelectionDialog.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.dialogs.FilteredTree; +import org.eclipse.ui.dialogs.PatternFilter; +import org.eclipse.ui.model.WorkbenchLabelProvider; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.ui.dialogs.AbstractMavenDialog; + + +/** + * A simple dialog allowing the selection of Maven projects and subfolders containing POMs. + */ +public class MavenProjectSelectionDialog extends AbstractMavenDialog { + + protected static final String DIALOG_SETTINGS = MavenProjectSelectionDialog.class.getName(); + + protected static final long SEARCH_DELAY = 500L; //in milliseconds + + private FilteredTree filteredTree; + + private boolean useCheckboxTree; + + /** Creates a dialog instance. */ + public MavenProjectSelectionDialog(Shell parent, boolean useCheckboxTree) { + this(parent); + this.useCheckboxTree = useCheckboxTree; + } + + /** Creates a dialog instance. */ + public MavenProjectSelectionDialog(Shell parent) { + super(parent, DIALOG_SETTINGS); + + setShellStyle(getShellStyle() | SWT.RESIZE); + setTitle(Messages.getString("projectSelectionDialog.title")); //$NON-NLS-1$ + } + + /** Produces the result of the selection. */ + protected void computeResult() { + if(useCheckboxTree) { + List<Object> result = new ArrayList<Object>(); + collectCheckedItems(getViewer().getTree().getItems(), result); + setResult(result); + } else { + setResult(((IStructuredSelection) getViewer().getSelection()).toList()); + } + } + + private void collectCheckedItems(TreeItem[] items, List<Object> list) { + for(TreeItem item : items) { + if(item.getChecked()) { + Object data = item.getData(); + if(data != null) { + list.add(data); + } + } + collectCheckedItems(item.getItems(), list); + } + } + + /** Creates the dialog controls. */ + protected Control createDialogArea(Composite parent) { + readSettings(); + + Composite composite = (Composite) super.createDialogArea(parent); + + filteredTree = new FilteredTree(composite, SWT.BORDER | (useCheckboxTree ? SWT.CHECK : 0), new PatternFilter(), + true); + filteredTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + getViewer().setContentProvider(new MavenContainerContentProvider()); + getViewer().setLabelProvider(WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider()); + getViewer().setInput(ResourcesPlugin.getWorkspace()); + + getViewer().addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + okPressed(); + } + }); + + return composite; + } + + protected void okPressed() { + super.okPressed(); + } + + protected TreeViewer getViewer() { + return filteredTree.getViewer(); + } + + /** The content provider class. */ + protected static class MavenContainerContentProvider implements ITreeContentProvider { + + /** Returns the children of the parent node. */ + public Object[] getChildren(Object parent) { + if(parent instanceof IWorkspace) { + IProject[] projects = ((IWorkspace) parent).getRoot().getProjects(); + + List<IProject> children = new ArrayList<IProject>(); + for(IProject project : projects) { + try { + if(project.isOpen() && project.hasNature(IMavenConstants.NATURE_ID)) { + children.add(project); + } + } catch(CoreException e) { + MavenLogger.log("Error checking project: " + e.getMessage(), e); + } + } + return children.toArray(); + } else if(parent instanceof IContainer) { + IContainer container = (IContainer) parent; + if(container.isAccessible()) { + try { + List<IResource> children = new ArrayList<IResource>(); + IResource[] members = container.members(); + for(int i = 0; i < members.length; i++ ) { + if(members[i] instanceof IContainer + && ((IContainer) members[i]).exists(new Path(IMavenConstants.POM_FILE_NAME))) { + children.add(members[i]); + } + } + return children.toArray(); + } catch(CoreException e) { + MavenLogger.log("Error checking container: " + e.getMessage(), e); + } + } + } + return new Object[0]; + } + + /** Returns the parent of the given element. */ + public Object getParent(Object element) { + if(element instanceof IResource) { + return ((IResource) element).getParent(); + } + return null; + } + + /** Returns true if the element has any children. */ + public boolean hasChildren(Object element) { + return getChildren(element).length > 0; + } + + /** Disposes of any resources used. */ + public void dispose() { + } + + /** Handles the input change. */ + public void inputChanged(Viewer viewer, Object arg1, Object arg2) { + } + + /** Returns the elements of the given root. */ + public Object[] getElements(Object element) { + return getChildren(element); + } + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizard.java new file mode 100644 index 00000000..48e3630d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizard.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.progress.IProgressConstants; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenMavenConsoleAction; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Simple project wizard for creating a new Maven2 project. + * <p> + * The wizard provides the following functionality to the user: + * <ul> + * <li>Create the project in the workspace or at some external location.</li> + * <li>Provide information about the Maven2 artifact to create.</li> + * <li>Choose directories of the default Maven2 directory structure to create.</li> + * <li>Choose a set of Maven2 dependencies for the project.</li> + * </ul> + * </p> + * <p> + * Once the wizard has finished, the following resources are created and configured: + * <ul> + * <li>A POM file containing the given artifact information and the chosen dependencies.</li> + * <li>The chosen Maven2 directories.</li> + * <li>The .classpath file is configured to hold appropriate entries for the Maven2 directories created as well as the + * Java and Maven2 classpath containers.</li> + * </ul> + * </p> + */ +public class MavenProjectWizard extends Wizard implements INewWizard { + + /** The wizard page for gathering general project information. */ + protected MavenProjectWizardLocationPage locationPage; + + /** The archetype selection page. */ + protected MavenProjectWizardArchetypePage archetypePage; + + /** The wizard page for gathering Maven2 project information. */ + protected MavenProjectWizardArtifactPage artifactPage; + + /** The wizard page for gathering archetype project information. */ + protected MavenProjectWizardArchetypeParametersPage parametersPage; + + /** The wizard page for choosing the Maven2 dependencies to use. */ + protected MavenDependenciesWizardPage dependenciesPage; + + ProjectImportConfiguration configuration; + + protected Button simpleProject; + + private IStructuredSelection selection; + + /** + * Default constructor. Sets the title and image of the wizard. + */ + public MavenProjectWizard() { + super(); + setWindowTitle(Messages.getString("wizard.project.title")); //$NON-NLS-1$ + setDefaultPageImageDescriptor(MavenImages.WIZ_NEW_PROJECT); + setNeedsProgressMonitor(true); + } + + public void init(IWorkbench workbench, IStructuredSelection selection) { + this.selection = selection; + } + + public void addPages() { + configuration = new ProjectImportConfiguration(); + configuration.setWorkingSet(SelectionUtil.getSelectedWorkingSet(selection)); + + locationPage = new MavenProjectWizardLocationPage(configuration, // + Messages.getString("wizard.project.page.project.title"), // //$NON-NLS-1$ + Messages.getString("wizard.project.page.project.description")) { //$NON-NLS-1$ + + protected void createAdditionalControls(Composite container) { + simpleProject = new Button(container, SWT.CHECK); + simpleProject.setText(Messages.getString("wizard.project.page.project.simpleProject")); //$NON-NLS-1$ + simpleProject.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + simpleProject.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + validate(); + } + }); + + Label label = new Label(container, SWT.NONE); + GridData labelData = new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1); + labelData.heightHint = 10; + label.setLayoutData(labelData); + } + + /** Skips the archetype selection page if the user chooses a simple project. */ + public IWizardPage getNextPage() { + return getPage(simpleProject.getSelection() ? "MavenProjectWizardArtifactPage" : "MavenProjectWizardArchetypePage"); //$NON-NLS-1$ //$NON-NLS-2$ + } + }; + locationPage.setLocationPath(SelectionUtil.getSelectedLocation(selection)); + + archetypePage = new MavenProjectWizardArchetypePage(configuration); + parametersPage = new MavenProjectWizardArchetypeParametersPage(configuration); + artifactPage = new MavenProjectWizardArtifactPage(configuration); + dependenciesPage = new MavenDependenciesWizardPage(configuration, // + Messages.getString("wizard.project.page.dependencies.title"), // //$NON-NLS-1$ + Messages.getString("wizard.project.page.dependencies.description")); //$NON-NLS-1$ + dependenciesPage.setDependencies(new Dependency[0]); + dependenciesPage.setShowScope(true); + + addPage(locationPage); + addPage(archetypePage); + addPage(parametersPage); + addPage(artifactPage); + addPage(dependenciesPage); + } + + /** Adds the listeners after the page controls are created. */ + public void createPageControls(Composite pageContainer) { + super.createPageControls(pageContainer); + + simpleProject.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + boolean isSimpleproject = simpleProject.getSelection(); + archetypePage.setUsed(!isSimpleproject); + parametersPage.setUsed(!isSimpleproject); + artifactPage.setUsed(isSimpleproject); + getContainer().updateButtons(); + } + }); + + archetypePage.addArchetypeSelectionListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent selectionchangedevent) { + parametersPage.setArchetype(archetypePage.getArchetype()); + getContainer().updateButtons(); + } + }); + +// locationPage.addProjectNameListener(new ModifyListener() { +// public void modifyText(ModifyEvent e) { +// parametersPage.setProjectName(locationPage.getProjectName()); +// artifactPage.setProjectName(locationPage.getProjectName()); +// } +// }); + } + + /** Returns the model. */ + public Model getModel() { + if(simpleProject.getSelection()) { + return artifactPage.getModel(); + } + return parametersPage.getModel(); + } + + /** + * To perform the actual project creation, an operation is created and run using this wizard as execution context. + * That way, messages about the progress of the project creation are displayed inside the wizard. + */ + public boolean performFinish() { + // First of all, we extract all the information from the wizard pages. + // Note that this should not be done inside the operation we will run + // since many of the wizard pages' methods can only be invoked from within + // the SWT event dispatcher thread. However, the operation spawns a new + // separate thread to perform the actual work, i.e. accessing SWT elements + // from within that thread would lead to an exception. + +// final IProject project = locationPage.getProjectHandle(); +// final String projectName = locationPage.getProjectName(); + + // Get the location where to create the project. For some reason, when using + // the default workspace location for a project, we have to pass null + // instead of the actual location. + final Model model = getModel(); + final String projectName = configuration.getProjectName(model); + IStatus nameStatus = configuration.validateProjectName(model); + if(!nameStatus.isOK()) { + MessageDialog.openError(getShell(), Messages.getString("wizard.project.job.failed", projectName), nameStatus.getMessage()); //$NON-NLS-1$ + return false; + } + + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + + final IPath location = locationPage.isInWorkspace() ? null : locationPage.getLocationPath(); + final IWorkspaceRoot root = workspace.getRoot(); + final IProject project = configuration.getProject(root, model); + + boolean pomExists = ( locationPage.isInWorkspace() ? + root.getLocation().append(project.getName()) : location ).append(IMavenConstants.POM_FILE_NAME).toFile().exists(); + if ( pomExists ) { + MessageDialog.openError(getShell(), Messages.getString("wizard.project.job.failed", projectName), Messages.getString("wizard.project.error.pomAlreadyExists")); //$NON-NLS-1$ //$NON-NLS-2$ + return false; + } + + final Job job; + + final MavenPlugin plugin = MavenPlugin.getDefault(); + + if(simpleProject.getSelection()) { + @SuppressWarnings("unchecked") + List<Dependency> modelDependencies = model.getDependencies(); + modelDependencies.addAll(Arrays.asList(dependenciesPage.getDependencies())); + + final String[] folders = artifactPage.getFolders(); + + job = new WorkspaceJob(Messages.getString("wizard.project.job.creatingProject", projectName)) { //$NON-NLS-1$ + public IStatus runInWorkspace(IProgressMonitor monitor) { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + try { + plugin.getProjectConfigurationManager().createSimpleProject(project, location, model, folders, // + configuration, monitor); + return Status.OK_STATUS; + } catch(CoreException e) { + return e.getStatus(); + } finally { + monitor.done(); + } + } + }; + + } else { + final Archetype archetype = archetypePage.getArchetype(); + + final String groupId = model.getGroupId(); + final String artifactId = model.getArtifactId(); + final String version = model.getVersion(); + final String javaPackage = parametersPage.getJavaPackage(); + final Properties properties = parametersPage.getProperties(); + + job = new WorkspaceJob(Messages.getString("wizard.project.job.creating", archetype.getArtifactId())) { //$NON-NLS-1$ + public IStatus runInWorkspace(IProgressMonitor monitor) { + setProperty(IProgressConstants.ACTION_PROPERTY, new OpenMavenConsoleAction()); + try { + plugin.getProjectConfigurationManager().createArchetypeProject(project, location, archetype, // + groupId, artifactId, version, javaPackage, properties, configuration, monitor); + return Status.OK_STATUS; + } catch(CoreException e) { + return e.getStatus(); + } finally { + monitor.done(); + } + } + }; + } + + job.addJobChangeListener(new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + final IStatus result = event.getResult(); + if(!result.isOK()) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + MessageDialog.openError(getShell(), // + Messages.getString("wizard.project.job.failed", projectName), result.getMessage()); //$NON-NLS-1$ + } + }); + } + } + }); + + + job.setRule(plugin.getProjectConfigurationManager().getRule()); + job.schedule(); + +// ProjectListener listener = new ProjectListener(); +// workspace.addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE); +// try { +// job.setRule(plugin.getProjectConfigurationManager().getRule()); +// job.schedule(); +// +// // MNGECLIPSE-766 wait until new project is created +// while(listener.getNewProject() == null && (job.getState() & (Job.WAITING | Job.RUNNING)) > 0) { +// try { +// Thread.sleep(100L); +// } catch (InterruptedException ex) { +// // ignore +// } +// } +// +// } finally { +// workspace.removeResourceChangeListener(listener); +// } + + return true; + } + + +// static class ProjectListener implements IResourceChangeListener { +// private IProject newProject = null; +// +// public void resourceChanged(IResourceChangeEvent event) { +// IResourceDelta root = event.getDelta(); +// IResourceDelta[] projectDeltas = root.getAffectedChildren(); +// for (int i = 0; i < projectDeltas.length; i++) { +// IResourceDelta delta = projectDeltas[i]; +// IResource resource = delta.getResource(); +// if (delta.getKind() == IResourceDelta.ADDED) { +// newProject = (IProject)resource; +// } +// } +// } +// /** +// * Gets the newProject. +// * @return Returns a IProject +// */ +// public IProject getNewProject() { +// return newProject; +// } +// } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypePage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypePage.java new file mode 100644 index 00000000..3e8ceb57 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypePage.java @@ -0,0 +1,953 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ComboViewer; +import org.eclipse.jface.viewers.IOpenListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.OpenEvent; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.ui.dialogs.PreferencesUtil; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.archetype.catalog.ArchetypeCatalog; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultArtifactRepository; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeCatalogFactory.NexusIndexerCatalogFactory; +import org.eclipse.m2e.core.archetype.ArchetypeManager; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.index.IMutableIndex; +import org.eclipse.m2e.core.index.IndexListener; +import org.eclipse.m2e.core.index.IndexManager; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.repository.IRepository; +import org.eclipse.m2e.core.util.M2EUtils; + + +/** + * Maven Archetype selection wizard page presents the user with a list of available Maven Archetypes available for + * creating new project. + */ +public class MavenProjectWizardArchetypePage extends AbstractMavenWizardPage implements IndexListener { + + private static final String KEY_CATALOG = "catalog"; //$NON-NLS-1$ + + private static final String ALL_CATALOGS = org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_all; + + public static final Comparator<Archetype> ARCHETYPE_COMPARATOR = new Comparator<Archetype>() { + + public int compare(Archetype a1, Archetype a2) { + String g1 = a1.getGroupId(); + String g2 = a2.getGroupId(); + int res = g1.compareTo(g2); + if(res != 0) { + return res; + } + + String i1 = a1.getArtifactId(); + String i2 = a2.getArtifactId(); + res = i1.compareTo(i2); + if(res != 0) { + return res; + } + + String v1 = a1.getVersion(); + String v2 = a2.getVersion(); + if(v1 == null) { + return v2 == null ? 0 : -1; + } + return v1.compareTo(v2); + } + + }; + + ComboViewer catalogsComboViewer; + + Text filterText; + + /** the archetype table viewer */ + TableViewer viewer; + + /** the description value label */ + Text descriptionText; + + Button showLastVersionButton; + + Button includeShapshotsButton; + + Button addArchetypeButton; + + /** the list of available archetypes */ + volatile Collection<Archetype> archetypes; + + Collection<Archetype> lastVersionArchetypes; + + /** a flag indicating if the archetype selection is actually used in the wizard */ + private boolean isUsed = true; + + ArchetypeCatalogFactory catalogFactory = null; + + /** + * Default constructor. Sets the title and description of this wizard page and marks it as not being complete as user + * input is required for continuing. + */ + public MavenProjectWizardArchetypePage(ProjectImportConfiguration projectImportConfiguration) { + super("MavenProjectWizardArchetypePage", projectImportConfiguration); //$NON-NLS-1$ + setTitle(Messages.getString("wizard.project.page.archetype.title")); //$NON-NLS-1$ + setDescription(Messages.getString("wizard.project.page.archetype.description")); //$NON-NLS-1$ + setPageComplete(false); + } + + /** Creates the page controls. */ + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(3, false)); + + createViewer(composite); + + createAdvancedSettings(composite, new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); + + MavenPlugin.getDefault().getIndexManager().addIndexListener(this); + setControl(composite); + } + + /** Creates the archetype table viewer. */ + private void createViewer(Composite parent) { + Label catalogsLabel = new Label(parent, SWT.NONE); + catalogsLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_lblCatalog); + + Composite catalogsComposite = new Composite(parent, SWT.NONE); + GridLayout catalogsCompositeLayout = new GridLayout(); + catalogsCompositeLayout.marginWidth = 0; + catalogsCompositeLayout.marginHeight = 0; + catalogsCompositeLayout.numColumns = 2; + catalogsComposite.setLayout(catalogsCompositeLayout); + catalogsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + + catalogsComboViewer = new ComboViewer(catalogsComposite); + catalogsComboViewer.getControl().setData("name", "catalogsCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + catalogsComboViewer.getCombo().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + catalogsComboViewer.setContentProvider(new IStructuredContentProvider() { + public Object[] getElements(Object input) { + + if(input instanceof Collection) { + return ((Collection<?>) input).toArray(); + } + return new Object[0]; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + }); + + catalogsComboViewer.setLabelProvider(new LabelProvider() { + public String getText(Object element) { + if(element instanceof ArchetypeCatalogFactory) { + return ((ArchetypeCatalogFactory) element).getDescription(); + } else if(element instanceof String) { + return element.toString(); + } + return super.getText(element); + } + }); + + catalogsComboViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + ISelection selection = event.getSelection(); + if(selection instanceof IStructuredSelection) { + Object factory = ((IStructuredSelection) selection).getFirstElement(); + if(factory instanceof ArchetypeCatalogFactory) { + catalogFactory = (ArchetypeCatalogFactory) factory; + } else if(factory instanceof String) { + catalogFactory = null; + } + reloadViewer(); + } else { + catalogFactory = null; + loadArchetypes(null, null, null); + } + //remember what was switched to here + if(dialogSettings != null && catalogFactory != null) { + dialogSettings.put(KEY_CATALOG, catalogFactory.getId()); + } + } + }); + + final ArchetypeManager archetypeManager = MavenPlugin.getDefault().getArchetypeManager(); + Collection<ArchetypeCatalogFactory> archetypeCatalogs = archetypeManager.getArchetypeCatalogs(); + ArrayList allCatalogs = new ArrayList(archetypeManager.getArchetypeCatalogs()); + allCatalogs.add(0, ALL_CATALOGS); + catalogsComboViewer.setInput(allCatalogs); + + Button configureButton = new Button(catalogsComposite, SWT.NONE); + configureButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + configureButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_btnConfigure); + configureButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + PreferencesUtil.createPreferenceDialogOn(getShell(), + "org.eclipse.m2e.preferences.MavenArchetypesPreferencePage", null, null).open(); //$NON-NLS-1$ + + if(catalogFactory == null || archetypeManager.getArchetypeCatalogFactory(catalogFactory.getId()) == null) { + catalogFactory = archetypeManager.getArchetypeCatalogFactory(NexusIndexerCatalogFactory.ID); + } + + catalogsComboViewer.setInput(archetypeManager.getArchetypeCatalogs()); + catalogsComboViewer.setSelection(new StructuredSelection(catalogFactory)); + } + }); + + Label filterLabel = new Label(parent, SWT.NONE); + filterLabel.setLayoutData(new GridData()); + filterLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_lblFilter); + + QuickViewerFilter quickViewerFilter = new QuickViewerFilter(); + LastVersionFilter versionFilter = new LastVersionFilter(); + IncludeSnapshotsFilter snapshotsFilter = new IncludeSnapshotsFilter(); + + filterText = new Text(parent, SWT.BORDER); + filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + filterText.addModifyListener(quickViewerFilter); + filterText.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if(e.keyCode == SWT.ARROW_DOWN) { + viewer.getTable().setFocus(); + viewer.getTable().setSelection(0); + + viewer.setSelection(new StructuredSelection(viewer.getElementAt(0)), true); + } + } + }); + + ToolBar toolBar = new ToolBar(parent, SWT.FLAT); + toolBar.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); + + final ToolItem clearToolItem = new ToolItem(toolBar, SWT.PUSH); + clearToolItem.setEnabled(false); + clearToolItem.setImage(MavenImages.IMG_CLEAR); + clearToolItem.setDisabledImage(MavenImages.IMG_CLEAR_DISABLED); + clearToolItem.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + filterText.setText(""); //$NON-NLS-1$ + } + }); + + filterText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + clearToolItem.setEnabled(filterText.getText().length() > 0); + } + }); + + SashForm sashForm = new SashForm(parent, SWT.VERTICAL); + GridData gd_sashForm = new GridData(SWT.FILL, SWT.FILL, false, true, 3, 1); + // gd_sashForm.widthHint = 500; + gd_sashForm.heightHint = 200; + sashForm.setLayoutData(gd_sashForm); + sashForm.setLayout(new GridLayout()); + + Composite composite1 = new Composite(sashForm, SWT.NONE); + GridLayout gridLayout1 = new GridLayout(); + gridLayout1.horizontalSpacing = 0; + gridLayout1.marginWidth = 0; + gridLayout1.marginHeight = 0; + composite1.setLayout(gridLayout1); + + viewer = new TableViewer(composite1, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION); + Table table = viewer.getTable(); + table.setData("name", "archetypesTable"); //$NON-NLS-1$ //$NON-NLS-2$ + table.setHeaderVisible(true); + + TableColumn column1 = new TableColumn(table, SWT.LEFT); + column1.setWidth(150); + column1.setText(Messages.getString("wizard.project.page.archetype.column.groupId")); //$NON-NLS-1$ + + TableColumn column0 = new TableColumn(table, SWT.LEFT); + column0.setWidth(150); + column0.setText(Messages.getString("wizard.project.page.archetype.column.artifactId")); //$NON-NLS-1$ + + TableColumn column2 = new TableColumn(table, SWT.LEFT); + column2.setWidth(100); + column2.setText(Messages.getString("wizard.project.page.archetype.column.version")); //$NON-NLS-1$ + + GridData tableData = new GridData(SWT.FILL, SWT.FILL, true, true); + tableData.widthHint = 400; + tableData.heightHint = 200; + table.setLayoutData(tableData); + + viewer.setLabelProvider(new ArchetypeLabelProvider()); + + viewer.setComparator(new ViewerComparator() { + public int compare(Viewer viewer, Object e1, Object e2) { + return ARCHETYPE_COMPARATOR.compare((Archetype) e1, (Archetype) e2); + } + }); + + viewer.addFilter(quickViewerFilter); + viewer.addFilter(versionFilter); + viewer.addFilter(snapshotsFilter); + + viewer.setContentProvider(new IStructuredContentProvider() { + public Object[] getElements(Object inputElement) { + if(inputElement instanceof Collection) { + return ((Collection<?>) inputElement).toArray(); + } + return new Object[0]; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + }); + + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Archetype archetype = getArchetype(); + if(archetype != null) { + String repositoryUrl = archetype.getRepository(); + String description = archetype.getDescription(); + + String text = description == null ? "" : description; //$NON-NLS-1$ + text = text.replaceAll("\n", "").replaceAll("\\s{2,}", " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + if(repositoryUrl != null) { + text += text.length() > 0 ? "\n" + repositoryUrl : repositoryUrl; //$NON-NLS-1$ + } + + descriptionText.setText(text); + setPageComplete(true); + } else { + descriptionText.setText(""); //$NON-NLS-1$ + setPageComplete(false); + } + } + }); + + viewer.addOpenListener(new IOpenListener() { + public void open(OpenEvent openevent) { + if(canFlipToNextPage()) { + getContainer().showPage(getNextPage()); + } + } + }); + + Composite composite2 = new Composite(sashForm, SWT.NONE); + GridLayout gridLayout2 = new GridLayout(); + gridLayout2.marginHeight = 0; + gridLayout2.marginWidth = 0; + gridLayout2.horizontalSpacing = 0; + composite2.setLayout(gridLayout2); + + descriptionText = new Text(composite2, SWT.WRAP | SWT.V_SCROLL | SWT.READ_ONLY | SWT.MULTI | SWT.BORDER); + + GridData descriptionTextData = new GridData(SWT.FILL, SWT.FILL, true, true); + descriptionTextData.heightHint = 40; + descriptionText.setLayoutData(descriptionTextData); + //whole dialog resizes badly without the width hint to the desc text + descriptionTextData.widthHint = 250; + sashForm.setWeights(new int[] {80, 20}); + + Composite buttonComposite = new Composite(parent, SWT.NONE); + GridData gd_buttonComposite = new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1); + buttonComposite.setLayoutData(gd_buttonComposite); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + gridLayout.numColumns = 3; + buttonComposite.setLayout(gridLayout); + + showLastVersionButton = new Button(buttonComposite, SWT.CHECK); + showLastVersionButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); + showLastVersionButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_btnLast); + showLastVersionButton.setSelection(true); + showLastVersionButton.addSelectionListener(versionFilter); + + includeShapshotsButton = new Button(buttonComposite, SWT.CHECK); + GridData buttonData = new GridData(SWT.LEFT, SWT.CENTER, true, false); + buttonData.horizontalIndent = 25; + includeShapshotsButton.setLayoutData(buttonData); + includeShapshotsButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_btnSnapshots); + includeShapshotsButton.setSelection(false); + includeShapshotsButton.addSelectionListener(snapshotsFilter); + + addArchetypeButton = new Button(buttonComposite, SWT.NONE); + addArchetypeButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_btnAdd); + addArchetypeButton.setData("name", "addArchetypeButton"); //$NON-NLS-1$ //$NON-NLS-2$ + buttonData = new GridData(SWT.RIGHT, SWT.CENTER, true, false); + buttonData.horizontalIndent = 35; + addArchetypeButton.setLayoutData(buttonData); + + addArchetypeButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + CustomArchetypeDialog dialog = new CustomArchetypeDialog(getShell(), + org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_add_title); + if(dialog.open() == Window.OK) { + String archetypeGroupId = dialog.getArchetypeGroupId(); + String archetypeArtifactId = dialog.getArchetypeArtifactId(); + String archetypeVersion = dialog.getArchetypeVersion(); + String repositoryUrl = dialog.getRepositoryUrl(); + downloadArchetype(archetypeGroupId, archetypeArtifactId, archetypeVersion, repositoryUrl); + } + } + }); + } + + protected IWizardContainer getContainer() { + return super.getContainer(); + } + + public void addArchetypeSelectionListener(ISelectionChangedListener listener) { + viewer.addSelectionChangedListener(listener); + } + + public void dispose() { + MavenPlugin.getDefault().getIndexManager().removeIndexListener(this); + super.dispose(); + } + + public List<Archetype> getArchetypesForCatalog() { + if(catalogFactory == null) { + return getAllArchetypes(); + } + try { + return catalogFactory.getArchetypeCatalog().getArchetypes(); + + } catch(CoreException ce) { + setErrorMessage(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_error_read); + return null; + } + } + + private List<Archetype> getAllArchetypes() { + ArchetypeManager manager = MavenPlugin.getDefault().getArchetypeManager(); + Collection<ArchetypeCatalogFactory> archetypeCatalogs = manager.getArchetypeCatalogs(); + ArrayList<Archetype> list = new ArrayList<Archetype>(); + + for(ArchetypeCatalogFactory catalog : archetypeCatalogs) { + try { + //temporary hack to get around 'Test Remote Catalog' blowing up on download + //described in https://issues.sonatype.org/browse/MNGECLIPSE-1792 + if(catalog.getDescription().startsWith("Test")) { //$NON-NLS-1$ + continue; + } + @SuppressWarnings("unchecked") + List arcs = catalog.getArchetypeCatalog().getArchetypes(); + if(arcs != null) { + list.addAll(arcs); + } + } catch(Exception ce) { + MavenLogger.log("Unable to read archetype catalog: " + catalog.getId(), ce); + } + } + return list; + } + + /** Loads the available archetypes. */ + void loadArchetypes(final String groupId, final String artifactId, final String version) { + Job job = new Job(Messages.getString("wizard.project.page.archetype.retrievingArchetypes")) { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + try { + List<Archetype> catalogArchetypes = getArchetypesForCatalog(); + + if(catalogArchetypes == null || catalogArchetypes.size() == 0) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + if(catalogFactory != null && "Nexus Indexer".equals(catalogFactory.getDescription())) { //$NON-NLS-1$ + setErrorMessage(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_error_no); + } + } + }); + } else { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + setErrorMessage(null); + } + }); + } + if(catalogArchetypes == null) { + return Status.CANCEL_STATUS; + } + TreeSet<Archetype> archs = new TreeSet<Archetype>(ARCHETYPE_COMPARATOR); + archs.addAll(catalogArchetypes); + archetypes = archs; + + Display.getDefault().asyncExec(new Runnable() { + public void run() { + updateViewer(groupId, artifactId, version); + } + }); + } catch(Exception e) { + monitor.done(); + return Status.CANCEL_STATUS; + } + + return Status.OK_STATUS; + } + }; + job.schedule(); + } + + public Set<Archetype> filterVersions(Collection<Archetype> archetypes) { + HashMap<String, Archetype> filteredArchetypes = new HashMap<String, Archetype>(); + + for(Archetype currentArchetype : archetypes) { + String key = getArchetypeKey(currentArchetype); + Archetype archetype = filteredArchetypes.get(key); + if(archetype == null) { + filteredArchetypes.put(key, currentArchetype); + } else { + DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(currentArchetype.getVersion()); + DefaultArtifactVersion version = new DefaultArtifactVersion(archetype.getVersion()); + if(currentVersion.compareTo(version) > 0) { + filteredArchetypes.put(key, currentArchetype); + } + } + } + + TreeSet<Archetype> result = new TreeSet<Archetype>(new Comparator<Archetype>() { + public int compare(Archetype a1, Archetype a2) { + String k1 = a1.getGroupId() + ":" + a1.getArtifactId() + ":" + a1.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + String k2 = a2.getGroupId() + ":" + a2.getArtifactId() + ":" + a2.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$ + return k1.compareTo(k2); + } + }); + result.addAll(filteredArchetypes.values()); + return result; + } + + private String getArchetypeKey(Archetype archetype) { + return archetype.getGroupId() + ":" + archetype.getArtifactId(); //$NON-NLS-1$ + } + + ArchetypeCatalog getArchetypeCatalog() throws CoreException { + return catalogFactory == null ? null : catalogFactory.getArchetypeCatalog(); + } + + /** Sets the flag that the archetype selection is used in the wizard. */ + public void setUsed(boolean isUsed) { + this.isUsed = isUsed; + } + + /** Overrides the default to return "true" if the page is not used. */ + public boolean isPageComplete() { + return !isUsed || super.isPageComplete(); + } + + /** Sets the focus to the table component. */ + public void setVisible(boolean visible) { + super.setVisible(visible); + + if(visible) { + ArchetypeManager archetypeManager = MavenPlugin.getDefault().getArchetypeManager(); + String catalogId = dialogSettings.get(KEY_CATALOG); + catalogFactory = null; + if(catalogId != null) { + catalogFactory = archetypeManager.getArchetypeCatalogFactory(catalogId); + } + catalogsComboViewer.setSelection(new StructuredSelection(catalogFactory == null ? ALL_CATALOGS : catalogFactory)); + + viewer.getTable().setFocus(); + Archetype selected = getArchetype(); + if(selected != null) { + viewer.reveal(selected); + } + } + } + + /** Returns the selected archetype. */ + public Archetype getArchetype() { + return (Archetype) ((IStructuredSelection) viewer.getSelection()).getFirstElement(); + } + + void updateViewer(String groupId, String artifactId, String version) { + lastVersionArchetypes = filterVersions(archetypes); + + viewer.setInput(archetypes); + + selectArchetype(groupId, artifactId, version); + + Table table = viewer.getTable(); + int columnCount = table.getColumnCount(); + int width = 0; + for(int i = 0; i < columnCount; i++ ) { + TableColumn column = table.getColumn(i); + column.pack(); + width += column.getWidth(); + } + GridData tableData = (GridData) table.getLayoutData(); + int oldHint = tableData.widthHint; + if(width > oldHint) { + tableData.widthHint = width; + } + getShell().pack(true); + tableData.widthHint = oldHint; + } + + protected void selectArchetype(String groupId, String artifactId, String version) { + Archetype archetype = findArchetype(groupId, artifactId, version); + + Table table = viewer.getTable(); + if(archetype != null) { + viewer.setSelection(new StructuredSelection(archetype), true); + + int n = table.getSelectionIndex(); + table.setSelection(n); + } + } + + /** Locates an archetype with given ids. */ + protected Archetype findArchetype(String groupId, String artifactId, String version) { + for(Archetype archetype : archetypes) { + if(archetype.getGroupId().equals(groupId) && archetype.getArtifactId().equals(artifactId)) { + if(version == null || version.equals(archetype.getVersion())) { + return archetype; + } + } + } + + return version == null ? null : findArchetype(groupId, artifactId, null); + } + + protected void downloadArchetype(final String archetypeGroupId, final String archetypeArtifactId, + final String archetypeVersion, final String repositoryUrl) { + final String archetypeName = archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion; //$NON-NLS-1$ //$NON-NLS-2$ + + try { + getContainer().run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InterruptedException { + monitor.beginTask(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_task_downloading + + archetypeName, IProgressMonitor.UNKNOWN); + + try { + final IMaven maven = MavenPlugin.getDefault().getMaven(); + + final MavenPlugin plugin = MavenPlugin.getDefault(); + + final List<ArtifactRepository> remoteRepositories; + if(repositoryUrl.length() == 0) { + remoteRepositories = maven.getArtifactRepositories(); // XXX should use ArchetypeManager.getArhetypeRepositories() + } else { + ArtifactRepository repository = new DefaultArtifactRepository( // + "archetype", repositoryUrl, new DefaultRepositoryLayout(), null, null); //$NON-NLS-1$ + remoteRepositories = Collections.singletonList(repository); + } + + monitor.subTask(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_task_resolving); + Artifact pomArtifact = maven.resolve(archetypeGroupId, archetypeArtifactId, archetypeVersion, + "pom", null, remoteRepositories, monitor); //$NON-NLS-1$ + monitor.worked(1); + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + File pomFile = pomArtifact.getFile(); + if(pomFile.exists()) { + monitor.subTask(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_task_resolving2); + Artifact jarArtifact = maven.resolve(archetypeGroupId, archetypeArtifactId, archetypeVersion, + "jar", null, remoteRepositories, monitor); //$NON-NLS-1$ + monitor.worked(1); + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + File jarFile = jarArtifact.getFile(); + + monitor.subTask(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_task_reading); + monitor.worked(1); + if(monitor.isCanceled()) { + throw new InterruptedException(); + } + + monitor.subTask(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_task_indexing); + IndexManager indexManager = plugin.getIndexManager(); + IMutableIndex localIndex = indexManager.getLocalIndex(); + localIndex.addArtifact(jarFile, new ArtifactKey(pomArtifact)); + + //save out the archetype + //TODO move this logig out of UI code! + Archetype archetype = new Archetype(); + archetype.setGroupId(archetypeGroupId); + archetype.setArtifactId(archetypeArtifactId); + archetype.setVersion(archetypeVersion); + archetype.setRepository(repositoryUrl); + org.apache.maven.archetype.Archetype archetyper = MavenPlugin.getDefault().getArchetype(); + archetyper.updateLocalCatalog(archetype); + + loadArchetypes(archetypeGroupId, archetypeArtifactId, archetypeVersion); + } else { + final Artifact pom = pomArtifact; + //the user tried to add an archetype that couldn't be resolved on the server + getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + setErrorMessage(NLS.bind( + org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_error_resolve, + pom.toString())); + } + }); + } + + } catch(InterruptedException ex) { + throw ex; + + } catch(final Exception ex) { + final String msg = NLS.bind( + org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_error_resolve2, archetypeName); + MavenLogger.log(msg, ex); + getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + setErrorMessage(msg + "\n" + ex.toString()); //$NON-NLS-1$ + } + }); + + } finally { + monitor.done(); + + } + } + }); + + } catch(InterruptedException ex) { + // ignore + + } catch(InvocationTargetException ex) { + String msg = NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypePage_error_resolve2, + archetypeName); + MavenLogger.log(msg, ex); + setErrorMessage(msg + "\n" + ex.toString()); //$NON-NLS-1$ + + } + } + + /** + * ArchetypeLabelProvider + */ + protected static class ArchetypeLabelProvider extends LabelProvider implements ITableLabelProvider { + /** Returns the element text */ + public String getColumnText(Object element, int columnIndex) { + if(element instanceof Archetype) { + Archetype archetype = (Archetype) element; + switch(columnIndex) { + case 0: + return archetype.getGroupId(); + case 1: + return archetype.getArtifactId(); + case 2: + return archetype.getVersion(); + } + } + return super.getText(element); + } + + /** Returns the element text */ + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + } + + /** + * QuickViewerFilter + */ + protected class QuickViewerFilter extends ViewerFilter implements ModifyListener { + + private String currentFilter; + + public boolean select(Viewer viewer, Object parentElement, Object element) { + if(currentFilter == null || currentFilter.length() == 0) { + return true; + } + Archetype archetype = (Archetype) element; + return archetype.getGroupId().indexOf(currentFilter) > -1 + || archetype.getArtifactId().indexOf(currentFilter) > -1; + } + + public void modifyText(ModifyEvent e) { + this.currentFilter = filterText.getText().trim(); + viewer.refresh(); + } + } + + protected class IncludeSnapshotsFilter extends ViewerFilter implements SelectionListener { + + /** + * + */ + private static final String SNAPSHOT = "SNAPSHOT"; //$NON-NLS-1$ + + private boolean includeSnapshots = false; + + public boolean select(Viewer viewer, Object parentElement, Object element) { + if(element instanceof Archetype) { + String version = ((Archetype) element).getVersion(); + boolean isSnap = false; + if(M2EUtils.nullOrEmpty(version)) { + isSnap = false; + } else if(version.lastIndexOf(SNAPSHOT) >= 0) { + isSnap = true; + } + return includeSnapshots ? true : !isSnap; + } + return false; + + } + + public void widgetSelected(SelectionEvent e) { + this.includeSnapshots = includeShapshotsButton.getSelection(); + viewer.refresh(); + Archetype archetype = getArchetype(); + //can be null in some cases, don't try to reveal + if(archetype != null) { + viewer.reveal(archetype); + } + viewer.getTable().setSelection(viewer.getTable().getSelectionIndex()); + viewer.getTable().setFocus(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + } + + protected class LastVersionFilter extends ViewerFilter implements SelectionListener { + + private boolean showLastVersion = true; + + public boolean select(Viewer viewer, Object parentElement, Object element) { + return showLastVersion ? lastVersionArchetypes.contains(element) : true; + } + + public void widgetSelected(SelectionEvent e) { + this.showLastVersion = showLastVersionButton.getSelection(); + viewer.refresh(); + Archetype archetype = getArchetype(); + //can be null in some cases, don't try to reveal + if(archetype != null) { + viewer.reveal(archetype); + } + viewer.getTable().setSelection(viewer.getTable().getSelectionIndex()); + viewer.getTable().setFocus(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.index.IndexListener#indexAdded(org.eclipse.m2e.repository.IRepository) + */ + public void indexAdded(IRepository repository) { + + } + + //reload the table when index updating finishes + //try to preserve selection in case this is a rebuild + protected void reloadViewer() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + if(isCurrentPage()) { + StructuredSelection sel = (StructuredSelection) viewer.getSelection(); + Archetype selArchetype = null; + if(sel != null && sel.getFirstElement() != null) { + selArchetype = (Archetype) sel.getFirstElement(); + } + if(selArchetype != null) { + loadArchetypes(selArchetype.getGroupId(), selArchetype.getArtifactId(), selArchetype.getVersion()); + } else { + loadArchetypes("org.apache.maven.archetypes", "maven-archetype-quickstart", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.index.IndexListener#indexChanged(org.eclipse.m2e.repository.IRepository) + */ + public void indexChanged(IRepository repository) { + reloadViewer(); + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.index.IndexListener#indexRemoved(org.eclipse.m2e.repository.IRepository) + */ + public void indexRemoved(IRepository repository) { + } + + /* (non-Javadoc) + * @see org.eclipse.m2e.index.IndexListener#indexUpdating(org.eclipse.m2e.repository.IRepository) + */ + public void indexUpdating(IRepository repository) { + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypeParametersPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypeParametersPage.java new file mode 100644 index 00000000..c566b6d4 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArchetypeParametersPage.java @@ -0,0 +1,617 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Pattern; + +import com.ibm.icu.lang.UCharacter; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; + +import org.apache.maven.archetype.catalog.Archetype; +import org.apache.maven.archetype.common.ArchetypeArtifactManager; +import org.apache.maven.archetype.exception.UnknownArchetype; +import org.apache.maven.archetype.metadata.ArchetypeDescriptor; +import org.apache.maven.archetype.metadata.RequiredProperty; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.ui.internal.components.TextComboBoxCellEditor; + + +/** + * Wizard page responsible for gathering information about the Maven2 artifact when an archetype is being used to create + * a project (thus the class name pun). + */ +public class MavenProjectWizardArchetypeParametersPage extends AbstractMavenWizardPage { + + public static final String DEFAULT_VERSION = "0.0.1-SNAPSHOT"; //$NON-NLS-1$ + + public static final String DEFAULT_PACKAGE = "foo"; //$NON-NLS-1$ + + Table propertiesTable; + + TableViewer propertiesViewer; + + final public static String KEY_PROPERTY = "key"; //$NON-NLS-1$ + + final public static int KEY_INDEX = 0; + + final public static String VALUE_PROPERTY = "value"; //$NON-NLS-1$ + + final public static int VALUE_INDEX = 1; + + /** group id text field */ + protected Combo groupIdCombo; + + /** artifact id text field */ + protected Combo artifactIdCombo; + + /** version text field */ + protected Combo versionCombo; + + /** package text field */ + protected Combo packageCombo; + + protected Button removeButton; + + private boolean isUsed = true; + + protected Set<String> requiredProperties; + + protected Set<String> optionalProperties; + + protected Archetype archetype; + + protected boolean archetypeChanged = false; + + /** shows if the package has been customized by the user */ + protected boolean packageCustomized = false; + + /** Creates a new page. */ + public MavenProjectWizardArchetypeParametersPage(ProjectImportConfiguration projectImportConfiguration) { + super("Maven2ProjectWizardArchifactPage", projectImportConfiguration); //$NON-NLS-1$ + + setTitle(Messages.getString("wizard.project.page.maven2.title")); //$NON-NLS-1$ + setDescription(Messages.getString("wizard.project.page.maven2.archetype.parameters.description")); //$NON-NLS-1$ + setPageComplete(false); + + requiredProperties = new HashSet<String>(); + optionalProperties = new HashSet<String>(); + } + + /** Creates page controls. */ + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + composite.setLayout(new GridLayout(3, false)); + + createArtifactGroup(composite); + createPropertiesGroup(composite); + + validate(); + + createAdvancedSettings(composite, new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); + resolverConfigurationComponent.setModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }); + + setControl(composite); + + } + + private void createArtifactGroup(Composite parent) { +// Composite artifactGroup = new Composite(parent, SWT.NONE); +// GridData gd_artifactGroup = new GridData( SWT.FILL, SWT.FILL, true, false ); +// artifactGroup.setLayoutData(gd_artifactGroup); +// artifactGroup.setLayout(new GridLayout(2, false)); + + Label groupIdlabel = new Label(parent, SWT.NONE); + groupIdlabel.setText(Messages.getString("artifactComponent.groupId")); //$NON-NLS-1$ + + groupIdCombo = new Combo(parent, SWT.BORDER); + groupIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + addFieldWithHistory("groupId", groupIdCombo); //$NON-NLS-1$ + groupIdCombo.setData("name", "groupId"); //$NON-NLS-1$ //$NON-NLS-2$ + groupIdCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateJavaPackage(); + validate(); + } + }); + + Label artifactIdLabel = new Label(parent, SWT.NONE); + artifactIdLabel.setText(Messages.getString("artifactComponent.artifactId")); //$NON-NLS-1$ + + artifactIdCombo = new Combo(parent, SWT.BORDER); + artifactIdCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + addFieldWithHistory("artifactId", artifactIdCombo); //$NON-NLS-1$ + artifactIdCombo.setData("name", "artifactId"); //$NON-NLS-1$ //$NON-NLS-2$ + artifactIdCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + updateJavaPackage(); + validate(); + } + }); + + Label versionLabel = new Label(parent, SWT.NONE); + versionLabel.setText(Messages.getString("artifactComponent.version")); //$NON-NLS-1$ + + versionCombo = new Combo(parent, SWT.BORDER); + GridData gd_versionCombo = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); + gd_versionCombo.widthHint = 150; + versionCombo.setLayoutData(gd_versionCombo); + versionCombo.setText(DEFAULT_VERSION); + addFieldWithHistory("version", versionCombo); //$NON-NLS-1$ + versionCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }); + + Label packageLabel = new Label(parent, SWT.NONE); + packageLabel.setText(Messages.getString("artifactComponent.package")); //$NON-NLS-1$ + + packageCombo = new Combo(parent, SWT.BORDER); + packageCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); + packageCombo.setData("name", "package"); //$NON-NLS-1$ //$NON-NLS-2$ + addFieldWithHistory("package", packageCombo); //$NON-NLS-1$ + packageCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + if(!packageCustomized && !packageCombo.getText().equals(getDefaultJavaPackage())) { + packageCustomized = true; + } + validate(); + } + }); + } + + private void createPropertiesGroup(Composite composite) { + Label propertiesLabel = new Label(composite, SWT.NONE); + propertiesLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); + propertiesLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_lblProps); + + propertiesViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION); + propertiesTable = propertiesViewer.getTable(); + propertiesTable.setLinesVisible(true); + propertiesTable.setHeaderVisible(true); + propertiesTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2)); + + TableColumn propertiesTableNameColumn = new TableColumn(propertiesTable, SWT.NONE); + propertiesTableNameColumn.setWidth(130); + propertiesTableNameColumn.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_columnName); + + TableColumn propertiesTableValueColumn = new TableColumn(propertiesTable, SWT.NONE); + propertiesTableValueColumn.setWidth(230); + propertiesTableValueColumn.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_columnValue); + + propertiesViewer.setColumnProperties(new String[] {KEY_PROPERTY, VALUE_PROPERTY}); + + propertiesViewer.setCellEditors(new CellEditor[] {new TextCellEditor(propertiesTable, SWT.NONE), + new TextCellEditor(propertiesTable, SWT.NONE)}); + propertiesViewer.setCellModifier(new ICellModifier() { + public boolean canModify(Object element, String property) { + return true; + } + + public void modify(Object element, String property, Object value) { + if(element instanceof TableItem) { + ((TableItem) element).setText(getTextIndex(property), String.valueOf(value)); + validate(); + } + } + + public Object getValue(Object element, String property) { + if(element instanceof TableItem) { + return ((TableItem) element).getText(getTextIndex(property)); + } + return null; + } + }); + + Button addButton = new Button(composite, SWT.NONE); + addButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + addButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_btnAdd); + addButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + propertiesViewer.editElement(addTableItem("?", "?"), KEY_INDEX); //$NON-NLS-1$ //$NON-NLS-2$ + } + }); + + removeButton = new Button(composite, SWT.NONE); + removeButton.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + removeButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_btnRemove); + removeButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + if(propertiesTable.getSelectionCount() > 0) { + propertiesTable.remove(propertiesTable.getSelectionIndices()); + removeButton.setEnabled(propertiesTable.getItemCount() > 0); + validate(); + } + } + }); + } + + /** + * Validates the contents of this wizard page. + * <p> + * Feedback about the validation is given to the user by displaying error messages or informative messages on the + * wizard page. Depending on the provided user input, the wizard page is marked as being complete or not. + * <p> + * If some error or missing input is detected in the user input, an error message or informative message, + * respectively, is displayed to the user. If the user input is complete and correct, the wizard page is marked as + * begin complete to allow the wizard to proceed. To that end, the following conditions must be met: + * <ul> + * <li>The user must have provided a valid group ID.</li> + * <li>The user must have provided a valid artifact ID.</li> + * <li>The user must have provided a version for the artifact.</li> + * </ul> + * </p> + * + * @see org.eclipse.jface.dialogs.DialogPage#setMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setErrorMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setPageComplete(boolean) + */ + void validate() { + String error = validateInput(); + setErrorMessage(error); + setPageComplete(error == null); + } + + private String validateInput() { + String error = validateIdInput(groupIdCombo.getText().trim(), "group"); //$NON-NLS-1$ + if(error != null) { + return error; + } + + error = validateIdInput(artifactIdCombo.getText().trim(), "artifact"); //$NON-NLS-1$ + if(error != null) { + return error; + } + + String versionValue = versionCombo.getText().trim(); + if(versionValue.length() == 0) { + return Messages.getString("wizard.project.page.maven2.validator.version"); //$NON-NLS-1$ + } + //TODO: check validity of version? + + String packageName = packageCombo.getText(); + if(packageName.trim().length() != 0) { + if(!Pattern.matches("[A-Za-z_$][A-Za-z_$\\d]*(?:\\.[A-Za-z_$][A-Za-z_$\\d]*)*", packageName)) { //$NON-NLS-1$ + return org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_error_package; + } + } + + // validate project name + IStatus nameStatus = getImportConfiguration().validateProjectName(getModel()); + if(!nameStatus.isOK()) { + return Messages.getString("wizard.project.page.maven2.validator.projectNameInvalid", nameStatus.getMessage()); //$NON-NLS-1$ + } + + if(requiredProperties.size() > 0) { + Properties properties = getProperties(); + for(String key : requiredProperties) { + String value = properties.getProperty(key); + if(value == null || value.length() == 0) { + return Messages.getString("wizard.project.page.maven2.validator.requiredProperty", key); //$NON-NLS-1$ + } + } + } + + return null; + } + + /** Ends the wizard flow chain. */ + public IWizardPage getNextPage() { + return null; + } + + public void setArchetype(Archetype archetype) { + if(archetype == null) { + propertiesTable.removeAll(); + archetypeChanged = false; + } else if(!archetype.equals(this.archetype)) { + this.archetype = archetype; + propertiesTable.removeAll(); + requiredProperties.clear(); + optionalProperties.clear(); + archetypeChanged = true; + + Properties properties = archetype.getProperties(); + if(properties != null) { + for(Iterator<Map.Entry<Object, Object>> it = properties.entrySet().iterator(); it.hasNext();) { + Map.Entry<?, ?> e = it.next(); + String key = (String) e.getKey(); + addTableItem(key, (String) e.getValue()); + optionalProperties.add(key); + } + } + } + } + + void loadArchetypeDescriptor() { + final String groupId = archetype.getGroupId(); + final String artifactId = archetype.getArtifactId(); + final String version = archetype.getVersion(); + final String archetypeName = groupId + ":" + artifactId + ":" + version; //$NON-NLS-1$ //$NON-NLS-2$ + + try { + getContainer().run(false, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + monitor.beginTask(NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_task, archetypeName), IProgressMonitor.UNKNOWN); + try { + IMaven maven = MavenPlugin.getDefault().getMaven(); + + ArtifactRepository localRepository = maven.getLocalRepository(); + + List<ArtifactRepository> repositories = maven.getArtifactRepositories(); + + ArchetypeArtifactManager aaMgr = MavenPlugin.getDefault().getArchetypeArtifactManager(); + if(aaMgr.isFileSetArchetype(groupId, artifactId, version, null, localRepository, repositories)) { + ArchetypeDescriptor descriptor = aaMgr.getFileSetArchetypeDescriptor(groupId, artifactId, version, null, + localRepository, repositories); + List<?> properties = descriptor.getRequiredProperties(); + if(properties != null) { + for(Object o : properties) { + if(o instanceof RequiredProperty) { + RequiredProperty rp = (RequiredProperty) o; + requiredProperties.add(rp.getKey()); + addTableItem(rp.getKey(), rp.getDefaultValue()); + } + } + } + } + } catch(UnknownArchetype e) { + MavenLogger.log(NLS.bind("Error downloading archetype {0}",archetypeName), e); + } catch(CoreException ex) { + MavenLogger.log(ex); + } finally { + monitor.done(); + } + } + }); + } catch(InterruptedException ex) { + // ignore + } catch(InvocationTargetException ex) { + String msg = NLS.bind(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArchetypeParametersPage_error_download, archetypeName); + MavenLogger.log(msg, ex); + setErrorMessage(msg + "\n" + ex.toString()); //$NON-NLS-1$ + } + } + + /** + * @param key + * @param value + */ + TableItem addTableItem(String key, String value) { + TableItem item = new TableItem(propertiesTable, SWT.NONE); + item.setData(item); + item.setText(KEY_INDEX, key); + item.setText(VALUE_INDEX, value == null ? "" : value); //$NON-NLS-1$ + return item; + } + + /** + * Updates the properties when a project name is set on the first page of the wizard. + */ + public void setProjectName(String projectName) { + if(artifactIdCombo.getText().equals(groupIdCombo.getText())) { + groupIdCombo.setText(projectName); + } + artifactIdCombo.setText(projectName); + packageCombo.setText("org." + projectName.replace('-', '.')); //$NON-NLS-1$ + validate(); + } + + /** + * Updates the properties when a project name is set on the first page of the wizard. + */ + public void setParentProject(String groupId, String artifactId, String version) { + groupIdCombo.setText(groupId); + versionCombo.setText(version); + validate(); + } + + /** Enables or disables the artifact id text field. */ + public void setArtifactIdEnabled(boolean b) { + artifactIdCombo.setEnabled(b); + } + + /** Returns the package name. */ + public String getJavaPackage() { + if(packageCombo.getText().length() > 0) { + return packageCombo.getText(); + } + return getDefaultJavaPackage(); + } + + /** Updates the package name if the related fields changed. */ + protected void updateJavaPackage() { + if(packageCustomized) { + return; + } + + String defaultPackageName = getDefaultJavaPackage(); + packageCombo.setText(defaultPackageName); + } + + /** Returns the default package name. */ + protected String getDefaultJavaPackage() { + return MavenProjectWizardArchetypeParametersPage.getDefaultJavaPackage(groupIdCombo.getText().trim(), + artifactIdCombo.getText().trim()); + } + + /** Creates the Model object. */ + public Model getModel() { + Model model = new Model(); + + model.setModelVersion("4.0.0"); //$NON-NLS-1$ + model.setGroupId(groupIdCombo.getText()); + model.setArtifactId(artifactIdCombo.getText()); + model.setVersion(versionCombo.getText()); + + return model; + } + + public void setUsed(boolean isUsed) { + this.isUsed = isUsed; + } + + public boolean isPageComplete() { + return !isUsed || super.isPageComplete(); + } + + /** Loads the group value when the page is displayed. */ + public void setVisible(boolean visible) { + super.setVisible(visible); + + if(visible) { + if(groupIdCombo.getText().length() == 0 && groupIdCombo.getItemCount() > 0) { + groupIdCombo.setText(groupIdCombo.getItem(0)); + packageCombo.setText(getDefaultJavaPackage()); + packageCustomized = false; + } + + if(archetypeChanged && archetype != null) { + archetypeChanged = false; + loadArchetypeDescriptor(); + validate(); + } + + updatePropertyEditors(); + } + } + + public Properties getProperties() { + if(propertiesViewer.isCellEditorActive()) { + propertiesTable.setFocus(); + } + Properties properties = new Properties(); + for(int i = 0; i < propertiesTable.getItemCount(); i++ ) { + TableItem item = propertiesTable.getItem(i); + properties.put(item.getText(KEY_INDEX), item.getText(VALUE_INDEX)); + } + return properties; + } + + public int getTextIndex(String property) { + return KEY_PROPERTY.equals(property) ? KEY_INDEX : VALUE_INDEX; + } + + public void updatePropertyEditors() { + CellEditor[] ce = propertiesViewer.getCellEditors(); + + int n = requiredProperties.size() + optionalProperties.size(); + if(n == 0) { + if(ce[KEY_INDEX] instanceof TextComboBoxCellEditor) { + // if there was a combo editor previously defined, and the current + // archetype has no properties, replace it with a plain text editor + ce[KEY_INDEX].dispose(); + ce[KEY_INDEX] = new TextCellEditor(propertiesTable, SWT.FLAT); + } + } else { + TextComboBoxCellEditor comboEditor = null; + // if there was a plain text editor previously defined, and the current + // archetype has properties, replace it with a combo editor + if(ce[KEY_INDEX] instanceof TextComboBoxCellEditor) { + comboEditor = (TextComboBoxCellEditor) ce[KEY_INDEX]; + } else { + ce[KEY_INDEX].dispose(); + comboEditor = new TextComboBoxCellEditor(propertiesTable, SWT.FLAT); + ce[KEY_INDEX] = comboEditor; + } + + // populate the property name selection + List<String> propertyKeys = new ArrayList<String>(n); + propertyKeys.addAll(requiredProperties); + propertyKeys.addAll(optionalProperties); + comboEditor.setItems(propertyKeys.toArray(new String[n])); + } + } + + public static String getDefaultJavaPackage(String groupId, String artifactId) { + StringBuffer sb = new StringBuffer(groupId); + + if(sb.length() > 0 && artifactId.length() > 0) { + sb.append('.'); + } + + sb.append(artifactId); + + if(sb.length() == 0) { + sb.append(DEFAULT_PACKAGE); + } + + boolean isFirst = true; + StringBuffer pkg = new StringBuffer(); + for(int i = 0; i < sb.length(); i++ ) { + char c = sb.charAt(i); + if(c == '-') { + pkg.append('_'); + isFirst = false; + } else { + if(isFirst) { + if(UCharacter.isJavaIdentifierStart(c)) { + pkg.append(c); + isFirst = false; + } + } else { + if(c == '.') { + pkg.append('.'); + isFirst = true; + } else if(UCharacter.isJavaIdentifierPart(c)) { + pkg.append(c); + } + } + } + } + + return pkg.toString(); + } +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArtifactPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArtifactPage.java new file mode 100644 index 00000000..aabcc22d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardArtifactPage.java @@ -0,0 +1,364 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; + +import org.apache.maven.model.Model; + +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; + + +/** + * Wizard page responsible for gathering information about the Maven2 artifact and the directories to create. This + * wizard page gathers Maven2 specific information. The user must specify the necessary information about the Maven2 + * artifact and she can also decide which directories of the default Maven2 directory structure should be created. Input + * validation is performed in order to make sure that all the provided information is valid before letting the wizard + * continue. + */ +public class MavenProjectWizardArtifactPage extends AbstractMavenWizardPage { + + private static final ProjectFolder JAVA = new ProjectFolder("src/main/java", "target/classes"); //$NON-NLS-1$ //$NON-NLS-2$ + + private static final ProjectFolder JAVA_TEST = new ProjectFolder("src/test/java", "target/test-classes"); //$NON-NLS-1$ //$NON-NLS-2$ + + private static final ProjectFolder RESOURCES = new ProjectFolder("src/main/resources", "target/classes"); //$NON-NLS-1$ //$NON-NLS-2$ + + private static final ProjectFolder RESOURCES_TEST = new ProjectFolder("src/test/resources", "target/test-classes"); //$NON-NLS-1$ //$NON-NLS-2$ + + // private static final ProjectFolder FILTERS = new ProjectFolder("src/main/filters", null, false); + + // private static final ProjectFolder FILTERS_TEST = new ProjectFolder("src/test/filters", null, false); + + // private static final ProjectFolder ASSEMBLY = new ProjectFolder("src/main/assembly", null, false); + + // private static final ProjectFolder CONFIG = new ProjectFolder("src/main/config", null, false); + + private static final ProjectFolder WEBAPP = new ProjectFolder("src/main/webapp", null); //$NON-NLS-1$ + + private static final ProjectFolder EAR = new ProjectFolder("src/main/application", null); //$NON-NLS-1$ + + private static final ProjectFolder SITE = new ProjectFolder("src/site", null); //$NON-NLS-1$ + + private static final ProjectFolder[] JAR_DIRS = {JAVA, JAVA_TEST, RESOURCES, RESOURCES_TEST}; + + private static final ProjectFolder[] WAR_DIRS = {JAVA, JAVA_TEST, RESOURCES, RESOURCES_TEST, WEBAPP}; + + private static final ProjectFolder[] EAR_DIRS = {EAR}; // MNGECLIPSE-688 add EAR Support + + private static final ProjectFolder[] POM_DIRS = {SITE}; + + /** special directory sets, default is JAR_DIRS */ + private static final Map<String, ProjectFolder[]> directorySets = new HashMap<String, ProjectFolder[]>(); + static { + directorySets.put(MavenArtifactComponent.WAR, WAR_DIRS); + directorySets.put(MavenArtifactComponent.POM, POM_DIRS); + directorySets.put(MavenArtifactComponent.EAR, EAR_DIRS); // MNGECLIPSE-688 add EAR Support + } + + /** parent property panel */ + protected MavenParentComponent parentComponent; + + /** artifact property panel */ + protected MavenArtifactComponent artifactComponent; + + /** the parent readonly state */ + private boolean readonlyParent = false; + + private boolean isUsed; + + /** + * Sets the title and description of this wizard page and marks it as not being complete as user input is required for + * continuing. + * @wbp.parser.constructor + */ + public MavenProjectWizardArtifactPage(ProjectImportConfiguration projectImportConfiguration) { + this("MavenProjectWizardArtifactPage", projectImportConfiguration); //$NON-NLS-1$ + } + + /** + * Sets the title and description of this wizard page and marks it as not being complete as user input is required for + * continuing. + */ + protected MavenProjectWizardArtifactPage(String name, ProjectImportConfiguration projectImportConfiguration) { + super(name, projectImportConfiguration); + + setTitle(Messages.getString("wizard.project.page.maven2.title")); //$NON-NLS-1$ + setDescription(Messages.getString("wizard.project.page.maven2.description")); //$NON-NLS-1$ + setPageComplete(false); + } + + /** + * {@inheritDoc} This wizard page contains a <code>MavenArtifactComponent</code> to gather information about the Maven + * artifact and a <code>Maven2DirectoriesComponent</code> which allows to choose which directories of the default + * Maven directory structure to create. + */ + public void createControl(Composite parent) { + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + + Composite container = new Composite(parent, SWT.NULL); + container.setLayout(layout); + + WidthGroup widthGroup = new WidthGroup(); + container.addControlListener(widthGroup); + + ModifyListener modifyingListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }; + + artifactComponent = new MavenArtifactComponent(container, SWT.NONE); + artifactComponent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + artifactComponent.setWidthGroup(widthGroup); + artifactComponent.setModifyingListener(modifyingListener); + artifactComponent.setArtifactIdEditable(!readonlyParent); + + parentComponent = new MavenParentComponent(container, readonlyParent ? SWT.READ_ONLY : SWT.NONE); + parentComponent.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); + parentComponent.setWidthGroup(widthGroup); + + parentComponent.addModifyListener(modifyingListener); + parentComponent.addBrowseButtonListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(getShell(), // + org.eclipse.m2e.core.internal.Messages.MavenProjectWizardArtifactPage_searchDialog_title, IIndex.SEARCH_PARENTS, Collections.<ArtifactKey> emptySet(), false); + + if(dialog.open() == Window.OK) { + IndexedArtifactFile indexedArtifactFile = (IndexedArtifactFile) dialog.getFirstResult(); + if(indexedArtifactFile != null) { + parentComponent.setValues(indexedArtifactFile.group, indexedArtifactFile.artifact, + indexedArtifactFile.version); + } + } + } + }); + + createAdvancedSettings(container, new GridData(SWT.FILL, SWT.TOP, false, false, 2, 1)); + resolverConfigurationComponent.setModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }); + + addFieldWithHistory("groupId", artifactComponent.getGroupIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("artifactId", artifactComponent.getArtifactIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("version", artifactComponent.getVersionCombo()); //$NON-NLS-1$ + addFieldWithHistory("name", artifactComponent.getNameCombo()); //$NON-NLS-1$ + + addFieldWithHistory("groupId", parentComponent.getGroupIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("artifactId", parentComponent.getArtifactIdCombo()); //$NON-NLS-1$ + addFieldWithHistory("version", parentComponent.getVersionCombo()); //$NON-NLS-1$ + + container.layout(); + + validate(); + + setControl(container); + } + + public void setVisible(boolean visible) { + super.setVisible(visible); + artifactComponent.getGroupIdCombo().setFocus(); + } + + /** + * Returns the Maven2 model containing the artifact information provided by the user. + * + * @return The Maven2 model containing the provided artifact information. Is never <code>null</code>. + */ + public Model getModel() { + Model model = artifactComponent.getModel(); + + parentComponent.updateModel(model); + + return model; + } + + /** Returns the list of directories for the currently selected packaging. */ + private ProjectFolder[] getProjectFolders() { + ProjectFolder[] folders = directorySets.get(artifactComponent.getPackaging()); + return folders == null ? JAR_DIRS : folders; + } + + /** + * Returns the directories of the default Maven2 directory structure selected by the user. These directories should be + * created along with the new project. + * + * @return The Maven2 directories selected by the user. Neither the array nor any of its elements is <code>null</code> + * . + */ + public String[] getFolders() { + ProjectFolder[] mavenDirectories = getProjectFolders(); + + String[] directories = new String[mavenDirectories.length]; + for(int i = 0; i < directories.length; i++ ) { + directories[i] = mavenDirectories[i].getPath(); + } + + return directories; + } + + /** + * Validates the contents of this wizard page. + * <p> + * Feedback about the validation is given to the user by displaying error messages or informative messages on the + * wizard page. Depending on the provided user input, the wizard page is marked as being complete or not. + * <p> + * If some error or missing input is detected in the user input, an error message or informative message, + * respectively, is displayed to the user. If the user input is complete and correct, the wizard page is marked as + * begin complete to allow the wizard to proceed. To that end, the following conditions must be met: + * <ul> + * <li>The user must have provided a group ID.</li> + * <li>The user must have provided an artifact ID.</li> + * <li>The user must have provided a version for the artifact.</li> + * <li>The user must have provided the packaging type for the artifact.</li> + * </ul> + * </p> + * + * @see org.eclipse.jface.dialogs.DialogPage#setMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setErrorMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setPageComplete(boolean) + */ + void validate() { + String error = validateInput(); + setErrorMessage(error); + setPageComplete(error == null); + } + + private String validateInput() { + String error = validateIdInput(artifactComponent.getGroupId().trim(), "group"); //$NON-NLS-1$ + if(error != null) { + return error; + } + + error = validateIdInput(artifactComponent.getArtifactId().trim(), "artifact"); //$NON-NLS-1$ + if(error != null) { + return error; + } + + if(artifactComponent.getVersion().trim().length() == 0) { + return Messages.getString("wizard.project.page.maven2.validator.version"); //$NON-NLS-1$ + } + + if(artifactComponent.getPackaging().trim().length() == 0) { + return Messages.getString("wizard.project.page.maven2.validator.packaging"); //$NON-NLS-1$ + } + + // if the parent project is specified, all three fields must be present + if(!parentComponent.validate()) { + return Messages.getString("wizard.project.page.maven2.validator.parent"); //$NON-NLS-1$ + } + + // validate project name + IStatus nameStatus = getImportConfiguration().validateProjectName(artifactComponent.getModel()); + if(!nameStatus.isOK()) { + return nameStatus.getMessage(); + } + + return null; + } + + /** + * Updates the properties when a project name is set on the first page of the wizard. + */ + public void setProjectName(String projectName) { + if(artifactComponent.getArtifactId().equals(artifactComponent.getGroupId())) { + artifactComponent.setGroupId(projectName); + } + artifactComponent.setArtifactId(projectName); + validate(); + } + + /** Sets the readonly parent flag. */ + public void setParentReadonly(boolean b) { + readonlyParent = b; + } + + /** + * Updates the properties when a project name is set on the first page of the wizard. + */ + public void setParentProject(String groupId, String artifactId, String version) { + parentComponent.setValues(groupId, artifactId, version); + artifactComponent.setGroupId(groupId); + artifactComponent.setVersion(version); + validate(); + } + + public void setUsed(boolean isUsed) { + this.isUsed = isUsed; + } + + public boolean isPageComplete() { + return !isUsed || super.isPageComplete(); + } + + /** + * A placeholder representing a directory in the project structure. + */ + final static class ProjectFolder { + + /** Folder path */ + private String path = null; + + /** Output path */ + private String outputPath = null; + + ProjectFolder(String path, String outputPath) { + this.path = path; + this.outputPath = outputPath; + } + + /** + * Returns folder path + */ + String getPath() { + return path; + } + + /** + * Returns folder output path + */ + String getOutputPath() { + return outputPath; + } + + /** + * Returns true for source folder + */ + boolean isSourceEntry() { + return this.getOutputPath() != null; + } + + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardLocationPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardLocationPage.java new file mode 100644 index 00000000..78f91edc --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/MavenProjectWizardLocationPage.java @@ -0,0 +1,321 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Wizard page used to specify project location and working set. + */ +public class MavenProjectWizardLocationPage extends AbstractMavenWizardPage { + + Button useDefaultWorkspaceLocationButton; + Label locationLabel; + Combo locationCombo; + + boolean initialized = false; + private WorkingSetGroup workingSetGroup; + + private IPath location; + + /** + * Creates Maven project location page. + * + * @param title location page title text + * @param description location page description text + */ + public MavenProjectWizardLocationPage(ProjectImportConfiguration configuration, String title, String description) { + super("MavenProjectWizardLocationPage", configuration); //$NON-NLS-1$ + setTitle(title); + setDescription(description); + setPageComplete(false); + + } + + /** + * Creates Maven project location page. + */ + public MavenProjectWizardLocationPage(ProjectImportConfiguration configuration, boolean showSimpleProject) { + super("MavenProjectWizardLocationPage", configuration); //$NON-NLS-1$ + setTitle(Messages.getString("wizard.project.page.project.title")); //$NON-NLS-1$ + setDescription(Messages.getString("wizard.project.page.project.description")); //$NON-NLS-1$ + setPageComplete(false); + } + + /** + * {@inheritDoc} This wizard page contains a component to query the project name and a + * <code>MavenLocationComponent</code> which allows to specify whether the project should be created in the + * workspace or at some given external location. + */ + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + container.setLayout(new GridLayout(3, false)); + + createAdditionalControls(container); + +// // project name +// GridData gridData = new GridData(); +// Label label = new Label(container, SWT.NULL); +// label.setLayoutData(gridData); +// label.setText(Messages.getString("wizard.project.page.project.projectName")); +// projectNameText = new Combo(container, SWT.BORDER | SWT.SINGLE); +// projectNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); +// projectNameText.addModifyListener(modifyingListener); +// addFieldWithHistory("projectName", projectNameText); + + // gridData.verticalIndent = 5; +// locationComponent = new MavenLocationComponent(container, SWT.NONE); +// locationComponent.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 3, 1)); +// locationComponent.setModifyingListener(modifyingListener); +// addFieldWithHistory("location", locationComponent.getLocationCombo()); + + useDefaultWorkspaceLocationButton = new Button(container, SWT.CHECK); + GridData useDefaultWorkspaceLocationButtonData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1); + useDefaultWorkspaceLocationButton.setLayoutData(useDefaultWorkspaceLocationButtonData); + useDefaultWorkspaceLocationButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardLocationPage_btnUserDefault); + useDefaultWorkspaceLocationButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + boolean inWorkspace = isInWorkspace(); + locationLabel.setEnabled(!inWorkspace); + locationCombo.setEnabled(!inWorkspace); + } + }); + useDefaultWorkspaceLocationButton.setSelection(true); + + locationLabel = new Label(container, SWT.NONE); + GridData locationLabelData = new GridData(); + locationLabelData.horizontalIndent = 10; + locationLabel.setLayoutData(locationLabelData); + locationLabel.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardLocationPage_lblLocation); + locationLabel.setEnabled(false); + + locationCombo = new Combo(container, SWT.NONE); + GridData locationComboData = new GridData(SWT.FILL, SWT.CENTER, true, false); + locationCombo.setLayoutData(locationComboData); + locationCombo.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + validate(); + } + }); + locationCombo.setEnabled(false); + addFieldWithHistory("location", locationCombo); //$NON-NLS-1$ + + Button locationBrowseButton = new Button(container, SWT.NONE); + GridData locationBrowseButtonData = new GridData(SWT.FILL, SWT.CENTER, false, false); + locationBrowseButton.setLayoutData(locationBrowseButtonData); + locationBrowseButton.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardLocationPage_btnLocation); + locationBrowseButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + DirectoryDialog dialog = new DirectoryDialog(getShell()); + dialog.setText(org.eclipse.m2e.core.internal.Messages.MavenProjectWizardLocationPage_dialog_location); + + String path = locationCombo.getText(); + if(path.length()==0) { + path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString(); + } + dialog.setFilterPath(path); + + String selectedDir = dialog.open(); + if(selectedDir != null) { + locationCombo.setText(selectedDir); + useDefaultWorkspaceLocationButton.setSelection(false); + validate(); + } + } + }); + + this.workingSetGroup = new WorkingSetGroup(container, getImportConfiguration(), getShell()); + + if(location==null || Platform.getLocation().equals(location)) { +// useDefaultWorkspaceLocationButton.setSelection(true); + } else { +// useDefaultWorkspaceLocationButton.setSelection(false); +// locationLabel.setEnabled(true); +// locationCombo.setEnabled(true); + locationCombo.setText(location.toOSString()); + } + + createAdvancedSettings(container, new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); + + setControl(container); + } + + /** + * Create additional controls + */ + protected void createAdditionalControls(Composite container) { + } + + public void dispose() { + super.dispose(); + workingSetGroup.dispose(); + } + + /** + * Returns whether the user has chosen to create the project in the workspace or at an external location. + * + * @return <code>true</code> if the project is to be created in the workspace, <code>false</code> if it should be + * created at an external location. + */ + public boolean isInWorkspace() { + return useDefaultWorkspaceLocationButton.getSelection(); + } + + /** + * Returns the path of the location where the project is to be created. According to the user input, the path either + * points to the workspace or to a valid user specified location on the filesystem. + * + * @return The path of the location where to create the project. Is never <code>null</code>. + */ + public IPath getLocationPath() { + if(isInWorkspace()) { + return ResourcesPlugin.getWorkspace().getRoot().getLocation(); + } + return Path.fromOSString(locationCombo.getText().trim()); + } + + public void setLocationPath(IPath location) { + this.location = location; + } + + /** {@inheritDoc} */ + public void setVisible(boolean visible) { + super.setVisible(visible); + + if(visible) { + initialized = true; + validate(); +// projectNameText.setFocus(); + } + } + + /** + * Validates the contents of this wizard page. + * <p> + * Feedback about the validation is given to the user by displaying error messages or informative messages on the + * wizard page. Depending on the provided user input, the wizard page is marked as being complete or not. + * <p> + * If some error or missing input is detected in the user input, an error message or informative message, + * respectively, is displayed to the user. If the user input is complete and correct, the wizard page is marked as + * begin complete to allow the wizard to proceed. To that end, the following conditions must be met: + * <ul> + * <li>The user must have provided a project name.</li> + * <li>The project name must be a valid project resource identifier.</li> + * <li>A project with the same name must not exist.</li> + * <li>A valid project location path must have been specified.</li> + * </ul> + * </p> + * + * @see org.eclipse.core.resources.IWorkspace#validateName(java.lang.String, int) + * @see org.eclipse.core.resources.IWorkspace#validateProjectLocation(org.eclipse.core.resources.IProject, + * org.eclipse.core.runtime.IPath) + * @see org.eclipse.jface.dialogs.DialogPage#setMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setErrorMessage(java.lang.String) + * @see org.eclipse.jface.wizard.WizardPage#setPageComplete(boolean) + */ + protected void validate() { + if (!initialized) { + return; + } + + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + +// final String name = getProjectName(); +// +// // check whether the project name field is empty +// if(name.trim().length() == 0) { +// setErrorMessage(null); +// setMessage(Messages.getString("wizard.project.page.project.validator.projectName")); +// setPageComplete(false); +// return; +// } +// +// // check whether the project name is valid +// final IStatus nameStatus = workspace.validateName(name, IResource.PROJECT); +// if(!nameStatus.isOK()) { +// setErrorMessage(nameStatus.getMessage()); +// setPageComplete(false); +// return; +// } +// +// // check whether project already exists +// final IProject handle = getProjectHandle(); +// if(handle.exists()) { +// setErrorMessage(Messages.getString("wizard.project.page.project.validator.projectExists")); +// setPageComplete(false); +// return; +// } + + IPath projectPath = getLocationPath(); + String location = projectPath.toOSString(); + + // check whether location is empty + if(location.length() == 0) { + setErrorMessage(null); + setMessage(Messages.getString("wizard.project.page.project.validator.projectLocation")); //$NON-NLS-1$ + setPageComplete(false); + return; + } + + // check whether the location is a syntactically correct path + if(!Path.ROOT.isValidPath(location)) { + setErrorMessage(Messages.getString("wizard.project.page.project.validator.invalidLocation")); //$NON-NLS-1$ + setPageComplete(false); + return; + } + + // If we do not place the contents in the workspace validate the location. + if(!isInWorkspace()) { + //this wizardpage is used in multiple wizards, not only in MavenProjectWizard + // the other wizard don't seem to have any getModel() methods. + //see MNGECLIPSE-1252 for more. + if (getWizard() instanceof MavenProjectWizard) + { + String projectName = getImportConfiguration().getProjectName(((MavenProjectWizard)getWizard()).getModel()); + if(projectName.length()>0){ + final IStatus locationStatus = workspace.validateProjectLocation(workspace.getRoot().getProject(projectName), projectPath); + if(!locationStatus.isOK()) { + setErrorMessage(locationStatus.getMessage()); + setPageComplete(false); + return; + } + } + } + } + + setPageComplete(true); + setErrorMessage(null); + setMessage(null); + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportPage.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportPage.java new file mode 100644 index 00000000..5200f7bf --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportPage.java @@ -0,0 +1,627 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.actions.WorkspaceModifyOperation; +import org.eclipse.ui.dialogs.IOverwriteQuery; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.Messages; + + +/** + * The ProjectsImportPage is the page that allows the user to import projects from a particular location. + */ +public class ProjectsImportPage extends WizardPage implements IOverwriteQuery { + + String location; + + CheckboxTreeViewer projectsList; + + IProject[] wsProjects; + + ProjectRecord[] selectedProjects = new ProjectRecord[0]; + + + public ProjectsImportPage(String location) { + super("wizardExternalProjectsPage"); //$NON-NLS-1$ + this.location = location; + + setTitle(Messages.ProjectsImportPage_title); + setDescription(Messages.ProjectsImportPage_desc); + setPageComplete(false); + } + + public void createControl(Composite parent) { + initializeDialogUnits(parent); + + Composite workArea = new Composite(parent, SWT.NONE); + setControl(workArea); + + workArea.setLayout(new GridLayout()); + workArea.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL)); + + createProjectsList(workArea); + createOptionsArea(workArea); + Dialog.applyDialogFont(workArea); + + updateProjectsList(location); + } + + /** + * Create the area with the extra options. + * + * @param workArea + */ + private void createOptionsArea(Composite workArea) { + } + + /** + * Create the checkbox list for the found projects. + * + * @param workArea + */ + private void createProjectsList(Composite workArea) { + Label title = new Label(workArea, SWT.NONE); + title.setText(Messages.ProjectsImportPage_lstProjects); + + Composite listComposite = new Composite(workArea, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + layout.marginWidth = 0; + layout.makeColumnsEqualWidth = false; + listComposite.setLayout(layout); + + listComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH)); + + projectsList = new CheckboxTreeViewer(listComposite, SWT.BORDER); + GridData listData = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH); + projectsList.getControl().setLayoutData(listData); + + projectsList.setContentProvider(new ITreeContentProvider() { + + public Object[] getChildren(Object parentElement) { + return null; + } + + public Object[] getElements(Object inputElement) { + return getValidProjects(); + } + + public boolean hasChildren(Object element) { + return false; + } + + public Object getParent(Object element) { + return null; + } + + public void dispose() { + + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + }); + + projectsList.setLabelProvider(new LabelProvider() { + public String getText(Object element) { + ProjectRecord projectRecord = (ProjectRecord) element; + return projectRecord.getProjectName() + " - " + projectRecord.projectFile.getParentFile().getAbsolutePath(); //$NON-NLS-1$ + } + }); + + projectsList.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + setPageComplete(projectsList.getCheckedElements().length > 0); + } + }); + + projectsList.setInput(this); + projectsList.setComparator(new ViewerComparator()); + createSelectionButtons(listComposite); + } + + /** + * Create the selection buttons in the listComposite. + * + * @param listComposite + */ + private void createSelectionButtons(Composite listComposite) { + Composite buttonsComposite = new Composite(listComposite, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + buttonsComposite.setLayout(layout); + + buttonsComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); + + Button selectAll = new Button(buttonsComposite, SWT.PUSH); + selectAll.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + selectAll.setText(Messages.ProjectsImportPage_btnSelect); + selectAll.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + projectsList.setCheckedElements(selectedProjects); + setPageComplete(projectsList.getCheckedElements().length > 0); + } + }); + Dialog.applyDialogFont(selectAll); + setButtonLayoutData(selectAll); + + Button deselectAll = new Button(buttonsComposite, SWT.PUSH); + deselectAll.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + deselectAll.setText(Messages.ProjectsImportPage_btnDeselect); + deselectAll.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + projectsList.setCheckedElements(new Object[0]); + setPageComplete(false); + } + }); + Dialog.applyDialogFont(deselectAll); + setButtonLayoutData(deselectAll); + + Button refresh = new Button(buttonsComposite, SWT.PUSH); + refresh.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + refresh.setText(Messages.ProjectsImportPage_btnRefresh); + refresh.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + updateProjectsList(location); + } + }); + Dialog.applyDialogFont(refresh); + setButtonLayoutData(refresh); + } + + /** + * Update the list of projects based on path. Method declared public only for test suite. + * + * @param path + */ + void updateProjectsList(final String path) { + try { + getContainer().run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + monitor.beginTask(Messages.ProjectsImportPage_task_search, 100); + File directory = new File(path); + selectedProjects = new ProjectRecord[0]; + Collection<File> files = new ArrayList<File>(); + monitor.worked(10); + + if(directory.isDirectory()) { + if(!collectProjectFilesFromDirectory(files, directory, null, monitor)) { + return; + } + selectedProjects = new ProjectRecord[files.size()]; + int index = 0; + monitor.worked(50); + monitor.subTask(Messages.ProjectsImportPage_task_processing); + for(File file : files) { + selectedProjects[index] = new ProjectRecord(file); + index++ ; + } + } else { + monitor.worked(60); + } + monitor.done(); + } + + }); + } catch(InvocationTargetException e) { + MavenLogger.log(e.getMessage(), e); + } catch(InterruptedException e) { + // Nothing to do if the user interrupts. + } + + projectsList.refresh(true); + projectsList.setCheckedElements(getValidProjects()); + if(getValidProjects().length < selectedProjects.length) { + setMessage(Messages.ProjectsImportPage_message, WARNING); + } else { + setMessage(null, WARNING); + } + setPageComplete(projectsList.getCheckedElements().length > 0); + } + + /** + * Collect the list of .project files that are under directory into files. + * + * @param files + * @param directory + * @param directoriesVisited Set of canonical paths of directories, used as recursion guard + * @param monitor The monitor to report to + * @return boolean <code>true</code> if the operation was completed. + */ + boolean collectProjectFilesFromDirectory(Collection<File> files, File directory, Set<String> directoriesVisited, + IProgressMonitor monitor) { + if(monitor.isCanceled()) { + return false; + } + + monitor.subTask(NLS.bind(Messages.ProjectsImportPage_task_checking, directory.getPath())); + File[] contents = directory.listFiles(); + if(contents == null) + return false; + + // Initialize recursion guard for recursive symbolic links + if(directoriesVisited == null) { + directoriesVisited = new HashSet<String>(); + try { + directoriesVisited.add(directory.getCanonicalPath()); + } catch(IOException exception) { + MavenLogger.log(exception.toString(), exception); + } + } + + // first look for project description files + final String dotProject = IProjectDescription.DESCRIPTION_FILE_NAME; + for(File file : contents) { + if(file.isFile() && file.getName().equals(dotProject)) { + files.add(file); + // don't search sub-directories since we can't have nested + // projects + return true; + } + } + + // no project description found, so recurse into sub-directories + for(File file : contents) { + if(file.isDirectory() && !IMavenConstants.METADATA_FOLDER.equals(file.getName())) { + try { + String canonicalPath = file.getCanonicalPath(); + if(!directoriesVisited.add(canonicalPath)) { + // already been here --> do not recurse + continue; + } + } catch(IOException exception) { + MavenLogger.log(exception.toString(), exception); + } + collectProjectFilesFromDirectory(files, file, directoriesVisited, monitor); + } + } + return true; + } + + /** + * Create the selected projects + * + * @return boolean <code>true</code> if all project creations were successful. + */ + public boolean createProjects() { + final Object[] selected = projectsList.getCheckedElements(); + + WorkspaceModifyOperation op = new WorkspaceModifyOperation() { + protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + monitor.beginTask("", selected.length); //$NON-NLS-1$ + if(monitor.isCanceled()) { + throw new OperationCanceledException(); + } + + for(int i = 0; i < selected.length; i++ ) { + createExistingProject((ProjectRecord) selected[i], new SubProgressMonitor(monitor, 1)); + } + } finally { + monitor.done(); + } + } + }; + + // run the new project creation operation + try { + getContainer().run(true, true, op); + } catch(InterruptedException e) { + return false; + } catch(InvocationTargetException e) { + // one of the steps resulted in a core exception + Throwable t = e.getTargetException(); + String message = Messages.ProjectsImportPage_error_creation; + IStatus status; + if(t instanceof CoreException) { + status = ((CoreException) t).getStatus(); + } else { + status = new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, 1, message, t); + } + ErrorDialog.openError(getShell(), message, null, status); + return false; + } + + return true; + } + + /** + * Performs clean-up if the user cancels the wizard without doing anything + */ + public void performCancel() { + } + + /** + * Create the project described in record. If it is successful return true. + * + * @param record + * @return boolean <code>true</code> if successful + * @throws InterruptedException + */ + boolean createExistingProject(final ProjectRecord record, IProgressMonitor monitor) + throws InvocationTargetException, InterruptedException { + String projectName = record.getProjectName(); + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + final IProject project = workspace.getRoot().getProject(projectName); + if(record.description == null) { + // error case + record.description = workspace.newProjectDescription(projectName); + IPath locationPath = new Path(record.projectFile.getAbsolutePath()); + + // If it is under the root use the default location + if(Platform.getLocation().isPrefixOf(locationPath)) { + record.description.setLocation(null); + } else { + record.description.setLocation(locationPath); + } + } else { + record.description.setName(projectName); + } + + try { + monitor.beginTask(Messages.ProjectsImportPage_task_creating, 100); + + @SuppressWarnings("deprecation") + IPath projectPath = record.description.getLocation(); + if(projectPath!=null) { + MavenConsole console = MavenPlugin.getDefault().getConsole(); + + IWorkspaceRoot root = workspace.getRoot(); + + if(projectPath.toFile().equals(root.getLocation().toFile())) { + console.logError("Can't create project " + projectName + " at Workspace folder"); + return false; + } + + if(projectPath.removeLastSegments(1).toFile().equals(root.getLocation().toFile())) { + // rename dir in workspace to match expected project name + if(!projectPath.equals(root.getLocation().append(projectName))) { + File projectDir = projectPath.toFile(); + File newProject = new File(projectDir.getParent(), projectName); + if(!projectDir.renameTo(newProject)) { + MavenLogger.log("Can't rename " + projectDir + " to " + newProject, null); + } + record.description.setLocation(null); + } + } + } + + project.create(record.description, new SubProgressMonitor(monitor, 30)); + project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 70)); + + } catch(CoreException e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + + return true; + } + + /** + * The <code>WizardDataTransfer</code> implementation of this <code>IOverwriteQuery</code> method asks the user + * whether the existing resource at the given path should be overwritten. + * + * @param pathString + * @return the user's reply: one of <code>"YES"</code>, <code>"NO"</code>, <code>"ALL"</code>, or + * <code>"CANCEL"</code> + */ + public String queryOverwrite(String pathString) { + Path path = new Path(pathString); + + String messageString; + // Break the message up if there is a file name and a directory + // and there are at least 2 segments. + if(path.getFileExtension() == null || path.segmentCount() < 2) { + messageString = NLS.bind(Messages.ProjectsImportPage_overwrite, pathString); + } else { + messageString = NLS.bind(Messages.ProjectsImportPage_overwrite2, + path.lastSegment(), path.removeLastSegments(1).toOSString()); + } + + final MessageDialog dialog = new MessageDialog(getContainer().getShell(), Messages.ProjectsImportPage_dialog_title, null, + messageString, MessageDialog.QUESTION, new String[] {IDialogConstants.YES_LABEL, + IDialogConstants.YES_TO_ALL_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.NO_TO_ALL_LABEL, + IDialogConstants.CANCEL_LABEL}, 0); + String[] response = new String[] {YES, ALL, NO, NO_ALL, CANCEL}; + // run in syncExec because callback is from an operation, + // which is probably not running in the UI thread. + getControl().getDisplay().syncExec(new Runnable() { + public void run() { + dialog.open(); + } + }); + return dialog.getReturnCode() < 0 ? CANCEL : response[dialog.getReturnCode()]; + } + + /** + * Retrieve all the projects in the current workspace. + * + * @return IProject[] array of IProject in the current workspace + */ + private IProject[] getProjectsInWorkspace() { + if(wsProjects == null) { + wsProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + } + return wsProjects; + } + + /** + * Method used for test suite. + * + * @return CheckboxTreeViewer the viewer containing all the projects found + */ + public CheckboxTreeViewer getProjectsList() { + return projectsList; + } + + /** + * Get the array of valid project records that can be imported from the source workspace or archive, selected by the + * user. If a project with the same name exists in both the source workspace and the current workspace, it will not + * appear in the list of projects to import and thus cannot be selected for import. Method declared public for test + * suite. + * + * @return ProjectRecord[] array of projects that can be imported into the workspace + */ + public ProjectRecord[] getValidProjects() { + List<ProjectRecord> validProjects = new ArrayList<ProjectRecord>(); + for(ProjectRecord projectRecord : selectedProjects) { + if(!isProjectInWorkspace(projectRecord.getProjectName())) { + validProjects.add(projectRecord); + } + } + return validProjects.toArray(new ProjectRecord[validProjects.size()]); + } + + /** + * Determine if the project with the given name is in the current workspace. + * + * @param projectName String the project name to check + * @return boolean true if the project with the given name is in this workspace + */ + private boolean isProjectInWorkspace(String projectName) { + if(projectName == null) { + return false; + } + IProject[] workspaceProjects = getProjectsInWorkspace(); + for(int i = 0; i < workspaceProjects.length; i++ ) { + if(projectName.equals(workspaceProjects[i].getName())) { + return true; + } + } + return false; + } + + + + /** + * Class declared public only for test suite. + */ + public static class ProjectRecord { + File projectFile; + + String projectName; + + IProjectDescription description; + + /** + * Create a record for a project based on the info in the file. + * + * @param file + */ + ProjectRecord(File file) { + projectFile = file; + setProjectName(); + } + + /** + * Set the name of the project based on the projectFile. + */ + private void setProjectName() { + IProjectDescription newDescription = null; + try { + IPath path = new Path(projectFile.getPath()); + // if the file is in the default location, use the directory + // name as the project name + newDescription = ResourcesPlugin.getWorkspace().loadProjectDescription(path); + + if(isDefaultLocation(path)) { + // projectName = path.segment(path.segmentCount() - 2); + // newDescription = ResourcesPlugin.getWorkspace().newProjectDescription(projectName); + } + } catch(CoreException e) { + // no good couldn't get the name + } + + if(newDescription == null) { + this.description = null; + projectName = ""; //$NON-NLS-1$ + } else { + this.description = newDescription; + projectName = this.description.getName(); + } + } + + /** + * Returns whether the given project description file path is in the default location for a project + * + * @param path The path to examine + * @return Whether the given path is the default location for a project + */ + private boolean isDefaultLocation(IPath path) { + // The project description file must at least be within the project, which is within the workspace location + return path.segmentCount() > 1 && path.removeLastSegments(2).toFile().equals(Platform.getLocation().toFile()); + } + + /** + * Get the name of the project + * + * @return String + */ + public String getProjectName() { + return projectName; + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportWizard.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportWizard.java new file mode 100644 index 00000000..5e3c563d --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ProjectsImportWizard.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.jface.wizard.Wizard; + +import org.eclipse.m2e.core.MavenImages; +import org.eclipse.m2e.core.internal.Messages; + +/** + * WizardExtension + * + * @author Eugene Kuleshov + */ +public class ProjectsImportWizard extends Wizard { + private final String location; + + private ProjectsImportPage mainPage; + + public ProjectsImportWizard(String location) { + this.location = location; + setWindowTitle(Messages.ProjectsImportWizard_title); + setDefaultPageImageDescriptor(MavenImages.WIZ_IMPORT_WIZ); + } + + public void addPages() { + mainPage = new ProjectsImportPage(this.location); + addPage(mainPage); + } + + public boolean performCancel() { + mainPage.performCancel(); + return true; + } + + public boolean performFinish() { + return mainPage.createProjects(); + } +}
\ No newline at end of file diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ResolverConfigurationComponent.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ResolverConfigurationComponent.java new file mode 100644 index 00000000..f94e4bff --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/ResolverConfigurationComponent.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.events.ExpansionAdapter; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.widgets.ExpandableComposite; + +import org.eclipse.m2e.core.core.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; +import org.eclipse.m2e.core.project.ResolverConfiguration; + + +/** + * A foldable resolver configuration panel + */ +public class ResolverConfigurationComponent extends ExpandableComposite { + + private static final String[] DEFAULT_NAME_TEMPLATES = {"[artifactId]", // //$NON-NLS-1$ + "[artifactId]-TRUNK", // //$NON-NLS-1$ + "[artifactId]-[version]", // //$NON-NLS-1$ + "[groupId].[artifactId]", "[groupId].[artifactId]-[version]"}; //$NON-NLS-1$ //$NON-NLS-2$ + + /** The resolver configuration */ + protected final ResolverConfiguration resolverConfiguration; + + /** project import configuration */ + private final ProjectImportConfiguration projectImportConfiguration; + + private ModifyListener modifyListener; + + Button resolveWorkspaceProjects; + + Text profiles; + + Combo template; + + /** Creates a new component. */ + public ResolverConfigurationComponent(final Composite parent, + final ProjectImportConfiguration propectImportConfiguration, final boolean enableProjectNameTemplate) { + super(parent, ExpandableComposite.COMPACT | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); + this.projectImportConfiguration = propectImportConfiguration; + this.resolverConfiguration = propectImportConfiguration.getResolverConfiguration(); + + setText(Messages.getString("resolverConfiguration.advanced")); //$NON-NLS-1$ + + final Composite advancedComposite = new Composite(this, SWT.NONE); + setClient(advancedComposite); + addExpansionListener(new ExpansionAdapter() { + public void expansionStateChanged(ExpansionEvent e) { + Shell shell = parent.getShell(); + Point minSize = shell.getMinimumSize(); + shell.setMinimumSize(shell.getSize().x, minSize.y); + shell.pack(); + parent.layout(); + shell.setMinimumSize(minSize); + } + }); + + GridLayout gridLayout = new GridLayout(); + gridLayout.marginLeft = 11; + gridLayout.numColumns = 2; + advancedComposite.setLayout(gridLayout); + + resolveWorkspaceProjects = new Button(advancedComposite, SWT.CHECK); + resolveWorkspaceProjects.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + resolveWorkspaceProjects.setText(Messages.getString("resolverConfiguration.resolveWorkspaceProjects")); //$NON-NLS-1$ + resolveWorkspaceProjects.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + resolverConfiguration.setResolveWorkspaceProjects(resolveWorkspaceProjects.getSelection()); + } + }); + + Label profilesLabel = new Label(advancedComposite, SWT.NONE); + profilesLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); + profilesLabel.setText(Messages.getString("resolverConfiguration.profiles")); //$NON-NLS-1$ + + profiles = new Text(advancedComposite, SWT.BORDER); + profiles.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + profiles.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + resolverConfiguration.setActiveProfiles(profiles.getText()); + } + }); + + if(enableProjectNameTemplate) { + Label templateLabel = new Label(advancedComposite, SWT.NONE); + templateLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); + templateLabel.setText(Messages.getString("resolverConfiguration.template")); //$NON-NLS-1$ + + template = new Combo(advancedComposite, SWT.BORDER); + template.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + template.setToolTipText(Messages.getString("resolverConfiguration.templateDescription")); //$NON-NLS-1$ + template.setItems(DEFAULT_NAME_TEMPLATES); + template.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + propectImportConfiguration.setProjectNameTemplate(template.getText()); + } + }); + } + + loadData(); + } + + public void loadData() { + resolveWorkspaceProjects.setSelection(resolverConfiguration.shouldResolveWorkspaceProjects()); + profiles.setText(resolverConfiguration.getActiveProfiles()); + if(template != null) { + template.setText(projectImportConfiguration.getProjectNameTemplate()); + } + } + + public ResolverConfiguration getResolverConfiguration() { + return this.resolverConfiguration; + } + + public void setModifyListener(ModifyListener modifyListener) { + this.modifyListener = modifyListener; + + if(template != null) { + template.addModifyListener(modifyListener); + } + } + + public void dispose() { + super.dispose(); + + if(modifyListener != null) { + template.removeModifyListener(modifyListener); + } + } + +} diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WidthGroup.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WidthGroup.java new file mode 100644 index 00000000..c6f8c77e --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WidthGroup.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.HashSet; + +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; + +/** + * Group of controls with the same width + * + * @author Eugene Kuleshov + */ +public class WidthGroup extends ControlAdapter { + + private final HashSet<Control> controls = new HashSet<Control>(); + + public void controlResized(ControlEvent e) { + int maxWidth = 0; + for(Control c : this.controls) { + int width = c.getSize().x; + if(width > maxWidth) { + maxWidth = width; + } + } + if(maxWidth > 0) { + for(Control c : this.controls) { + GridData gd = (GridData) c.getLayoutData(); + gd.widthHint = maxWidth; + c.getParent().layout(); + } + } + } + + public void addControl(Control control) { + controls.add(control); + control.getParent().layout(); + } + +} + diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WorkingSetGroup.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WorkingSetGroup.java new file mode 100644 index 00000000..0d7bf352 --- /dev/null +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/wizards/WorkingSetGroup.java @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 Sonatype, Inc. + * 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: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.core.wizards; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.resource.DeviceResourceException; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.jface.viewers.ComboViewer; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkingSet; +import org.eclipse.ui.IWorkingSetManager; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.IWorkingSetSelectionDialog; + +import org.eclipse.m2e.core.internal.Messages; +import org.eclipse.m2e.core.project.ProjectImportConfiguration; + + +/** + * Working set group + * + * @author Eugene Kuleshov + */ +public class WorkingSetGroup { + + static final List<String> WORKING_SET_IDS = Arrays.asList( // + "org.eclipse.ui.resourceWorkingSetPage", "org.eclipse.jdt.ui.JavaWorkingSetPage"); //$NON-NLS-1$ //$NON-NLS-2$ + + ComboViewer workingsetComboViewer; + + Button addToWorkingSetButton; + + final ProjectImportConfiguration configuration; + + final Shell shell; + + public WorkingSetGroup(Composite container, ProjectImportConfiguration configuration, Shell shell) { + this.configuration = configuration; + this.shell = shell; + + createControl(container); + } + + private void createControl(Composite container) { + addToWorkingSetButton = new Button(container, SWT.CHECK); + GridData gd_addToWorkingSetButton = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1); + gd_addToWorkingSetButton.verticalIndent = 12; + addToWorkingSetButton.setLayoutData(gd_addToWorkingSetButton); + addToWorkingSetButton.setSelection(true); + addToWorkingSetButton.setData("name", "addToWorkingSetButton"); //$NON-NLS-1$ //$NON-NLS-2$ + addToWorkingSetButton.setText(Messages.WorkingSetGroup_btnAddSet); + addToWorkingSetButton.setSelection(false); + + final Label workingsetLabel = new Label(container, SWT.NONE); + GridData gd_workingsetLabel = new GridData(); + gd_workingsetLabel.horizontalIndent = 10; + workingsetLabel.setLayoutData(gd_workingsetLabel); + workingsetLabel.setEnabled(false); + workingsetLabel.setData("name", "workingsetLabel"); //$NON-NLS-1$ //$NON-NLS-2$ + workingsetLabel.setText(Messages.WorkingSetGroup_lblSet); + + Combo workingsetCombo = new Combo(container, SWT.READ_ONLY); + workingsetCombo.setEnabled(false); + workingsetCombo.setData("name", "workingsetCombo"); //$NON-NLS-1$ //$NON-NLS-2$ + workingsetCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + workingsetComboViewer = new ComboViewer(workingsetCombo); + workingsetComboViewer.setContentProvider(new IStructuredContentProvider() { + public Object[] getElements(Object input) { + if(input instanceof IWorkingSet[]) { + return (IWorkingSet[]) input; + } else if(input instanceof List<?>) { + return new Object[] {input}; + } else if(input instanceof Set<?>) { + return ((Set<?>) input).toArray(); + } + return new IWorkingSet[0]; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public void dispose() { + } + }); + workingsetComboViewer.setLabelProvider(new LabelProvider() { + private ResourceManager images = new LocalResourceManager(JFaceResources.getResources()); + + @SuppressWarnings("deprecation") + public Image getImage(Object element) { + if(element instanceof IWorkingSet) { + ImageDescriptor imageDescriptor = ((IWorkingSet) element).getImage(); + if(imageDescriptor != null) { + try { + return (Image) images.create(imageDescriptor); + } catch(DeviceResourceException ex) { + return null; + } + } + } + return super.getImage(element); + } + + public String getText(Object element) { + if(element instanceof IWorkingSet) { + return ((IWorkingSet) element).getLabel(); + } else if(element instanceof List<?>) { + StringBuffer sb = new StringBuffer(); + for(Object o : (List<?>) element) { + if(o instanceof IWorkingSet) { + if(sb.length() > 0) { + sb.append(", "); //$NON-NLS-1$ + } + sb.append(((IWorkingSet) o).getLabel()); + } + } + return sb.toString(); + } + return super.getText(element); + } + + public void dispose() { + images.dispose(); + super.dispose(); + } + }); + + workingsetComboViewer.setComparator(new ViewerComparator()); + + final Button newWorkingSetButton = new Button(container, SWT.NONE); + newWorkingSetButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); + newWorkingSetButton.setData("name", "configureButton"); //$NON-NLS-1$ //$NON-NLS-2$ + newWorkingSetButton.setText(Messages.WorkingSetGroup_btnMore); + newWorkingSetButton.setEnabled(false); + newWorkingSetButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(final SelectionEvent e) { + IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager(); + IWorkingSetSelectionDialog dialog = workingSetManager.createWorkingSetSelectionDialog(shell, true, + WORKING_SET_IDS.toArray(new String[0])); + if(dialog.open() == Window.OK) { + IWorkingSet[] workingSets = dialog.getSelection(); + + selectWorkingSets(workingSets); + } + } + }); + + if(selectWorkingSets(configuration.getWorkingSets())) { + addToWorkingSetButton.setSelection(true); + workingsetLabel.setEnabled(true); + workingsetComboViewer.getCombo().setEnabled(true); + newWorkingSetButton.setEnabled(true); + } + + addToWorkingSetButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + boolean addToWorkingingSet = addToWorkingSetButton.getSelection(); + workingsetLabel.setEnabled(addToWorkingingSet); + workingsetComboViewer.getCombo().setEnabled(addToWorkingingSet); + newWorkingSetButton.setEnabled(addToWorkingingSet); + if(addToWorkingingSet) { + updateConfiguration(); + } else { + configuration.setWorkingSet(null); + } + } + }); + + workingsetComboViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateConfiguration(); + } + }); + } + + protected void updateConfiguration() { + if(addToWorkingSetButton.getSelection()) { + IStructuredSelection selection = (IStructuredSelection) workingsetComboViewer.getSelection(); + Object o = selection.getFirstElement(); + if(o != null) { + if(o instanceof IWorkingSet) { + configuration.setWorkingSet((IWorkingSet) o); + } else if(o instanceof List<?>) { + List<?> l = (List<?>) o; + configuration.setWorkingSets(l.toArray(new IWorkingSet[l.size()])); + } + } + } + } + + Set<IWorkingSet> getWorkingSets() { + Set<IWorkingSet> workingSets = new HashSet<IWorkingSet>(); + + IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager(); + for(IWorkingSet workingSet : workingSetManager.getWorkingSets()) { + if(!workingSet.isEmpty()) { + IAdaptable[] elements = workingSet.getElements(); + IResource resource = (IResource) elements[0].getAdapter(IResource.class); + if(resource != null) { + workingSets.add(workingSet); + } + } else { + if(WORKING_SET_IDS.contains(workingSet.getId())) { + workingSets.add(workingSet); + } + } + } + + return workingSets; + } + + public void dispose() { + workingsetComboViewer.getLabelProvider().dispose(); + } + + protected boolean selectWorkingSets(IWorkingSet[] workingSets) { + Set<IWorkingSet> defaultSets = getWorkingSets(); + workingsetComboViewer.setInput(defaultSets); + + if(workingSets != null && workingSets.length > 0) { + if(workingSets.length == 1) { + IWorkingSet workingSet = workingSets[0]; + if(defaultSets.contains(workingSet)) { + workingsetComboViewer.setSelection(new StructuredSelection(workingSet)); + } + } else { + List<?> list = Arrays.asList(workingSets); + workingsetComboViewer.add(list); + workingsetComboViewer.setSelection(new StructuredSelection((Object) list)); + } + return true; + } + return false; + } +} |