/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Dan Rubel - project set serializer API *******************************************************************************/ package org.eclipse.team.core; import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.team.internal.core.Messages; /** * An object for serializing and deserializing * references to projects. Given a project, it can produce a * UTF-8 encoded String which can be stored in a file. * Given this String, it can load a project into the workspace. * It also provides a mechanism * by which repository providers can be notified when a project set is created and exported. * * @see RepositoryProviderType * * @since 2.1 */ public abstract class ProjectSetCapability { /** * Scheme constant (value "scm") indicating the SCM URI. * * @since 3.6 */ public static final String SCHEME_SCM = "scm"; //$NON-NLS-1$ /** * Ensure that the provider type is backwards compatible by * passing the project set serializer to the type if a serializer * is registered. This is required for repository providers * who implemented a project set capability in 2.1 (before the * capability contained the serialization API) and have not * released a 3.0 plugin yet. This method is * called before project set export and import and can be used by * other clients who work with project sets. * * @param type the provider type instance * @param capability the capability that was obtained from the provider type * * @since 3.0 */ public static void ensureBackwardsCompatible(RepositoryProviderType type, ProjectSetCapability capability) { if (capability != null) { IProjectSetSerializer oldSerializer = Team.getProjectSetSerializer(type.getID()); if (oldSerializer != null) { capability.setSerializer(oldSerializer); } } } /** * The old serialization interface */ private IProjectSetSerializer serializer; /** * Notify the provider that a project set has been created at path. Only * providers identified as having projects in the project set will be * notified. The project set may or may not be created in a workspace * project (thus may not be a resource). * * @param file the project set file that was created * @param context a UI context object. This object will either be a * com.ibm.swt.widgets.Shell or it will be null. * @param monitor a progress monitor * * @deprecated should use or override * projectSetCreated(File, ProjectSetSerializationContext, IProgressMonitor) * instead */ @Deprecated public void projectSetCreated(File file, Object context, IProgressMonitor monitor) { //default is to do nothing } /** * Notify the provider that a project set has been created at path. Only * providers identified as having projects in the project set will be * notified. The project set may or may not be created in a workspace * project (thus may not be a resource). * * @param file the project set file that was created * @param context * the context in which the references are created * (not null) * @param monitor a progress monitor * * @since 3.0 */ public void projectSetCreated(File file, ProjectSetSerializationContext context, IProgressMonitor monitor) { // Invoke old method by default projectSetCreated(file, context.getShell(), monitor); } /** * For every project in providerProjects, return an opaque * UTF-8 encoded String to act as a reference to that project. * The format of the String is specific to the provider. * The format of the String must be such that * {@link #addToWorkspace(String[], ProjectSetSerializationContext, IProgressMonitor)} * will be able to consume it and load the corresponding project. *

* This default implementation simply throws an exception * indicating that no references can be created unless there * is an IProjectSetSerializer registered for the repository * provider type in which case the operation is delegated to the * serializer. * Subclasses are expected to override. * * @since 3.0 * * @param providerProjects * an array of projects for which references are needed * (not null and contains no nulls) * @param context * the context in which the references are created * (not null) * @param monitor * a progress monitor or null if none * @return * an array containing exactly the same number of elements * as the providerProjects argument * where each element is a serialized reference string * uniquely identifying the corresponding the project in the providerProjects array * (not null and contains no nulls) * @throws TeamException * thrown if there is a reference string cannot be created for a project */ public String[] asReference( IProject[] providerProjects, ProjectSetSerializationContext context, IProgressMonitor monitor) throws TeamException { if (serializer != null) { return serializer.asReference(providerProjects, context.getShell(), monitor); } throw new TeamException(Messages.ProjectSetCapability_0); } /** * For every String in referenceStrings, load the corresponding project into the workspace. * The opaque strings in referenceStrings are guaranteed to have been previously * produced by {@link #asReference(IProject[], ProjectSetSerializationContext, IProgressMonitor)}. * The {@link #confirmOverwrite(ProjectSetSerializationContext, IProject[])} method is called with an array of projects * for which projects of the same name already exists in the workspace. *

* Callers from within a UI context should wrap a call to this method * inside a WorkspaceModifyOperation so that events generated as a result * of this operation are deferred until the outermost operation * has successfully completed. *

* This default implementation simply throws an exception * indicating that no projects can be loaded unless there * is an {@link IProjectSetSerializer} registered for the repository * provider type in which case the operation is delegated to the * serializer. * Subclasses are expected to override. * * @since 3.0 * * @param referenceStrings * an array of reference strings uniquely identifying the projects * (not null and contains no nulls) * @param context * the context in which the projects are loaded * (not null) * @param monitor * a progress monitor or null if none * @return IProject[] * an array of projects that were loaded * excluding those projects already existing and not overwritten * (not null, contains no nulls) * @throws TeamException * thrown if there is a problem loading a project into the workspace. * If an exception is thrown, then the workspace is left in an unspecified state * where some of the referenced projects may be loaded or partially loaded, and others may not. */ public IProject[] addToWorkspace( String[] referenceStrings, ProjectSetSerializationContext context, IProgressMonitor monitor) throws TeamException { if (serializer != null) { return serializer.addToWorkspace(referenceStrings, context.getFilename(), context.getShell(), monitor); } throw new TeamException(Messages.ProjectSetCapability_1); } //////////////////////////////////////////////////////////////////////////// // // Internal utility methods for subclasses // //////////////////////////////////////////////////////////////////////////// /** * Determine if any of the projects already exist * and confirm which of those projects are to be overwritten. * * @since 3.0 * * @param context * the context in which the projects are loaded * (not null) * @param projects * an array of proposed projects to be loaded * (not null, contains no nulls) * @return * an array of confirmed projects to be loaded * or null if the operation is to be canceled. * @throws TeamException */ protected IProject[] confirmOverwrite( ProjectSetSerializationContext context, IProject[] projects) throws TeamException { // Build a collection of existing projects final Collection existingProjects = new ArrayList<>(); for (int i = 0; i < projects.length; i++) { IProject eachProj = projects[i]; if (eachProj.exists()) { existingProjects.add(eachProj); } else if (new File(eachProj.getParent().getLocation().toFile(), eachProj.getName()).exists()) { existingProjects.add(eachProj); } } if (existingProjects.size() == 0) return projects; // Confirm the overwrite IProject[] confirmed = context.confirmOverwrite( existingProjects.toArray( new IProject[existingProjects.size()])); if (confirmed == null) return null; if (existingProjects.size() == confirmed.length) return projects; // Return the amended list of projects to be loaded Collection result = new ArrayList<>(projects.length); result.addAll(Arrays.asList(projects)); result.removeAll(existingProjects); for (int i = 0; i < confirmed.length; i++) { IProject eachProj = confirmed[i]; if (existingProjects.contains(eachProj)) result.add(eachProj); } return result.toArray(new IProject[result.size()]); } /* * Set the serializer to the one registered. The serializer * will be used if subclasses do not override asReference * and addToWorkspace */ void setSerializer(IProjectSetSerializer serializer) { this.serializer = serializer; } /** * Return the URI for the given reference string or null * if this capability does not support file system schemes as defined by * the org.eclipse.core.filesystem.filesystems extension * point. * @see #getProject(String) * @param referenceString a reference string obtained from * {@link #asReference(IProject[], ProjectSetSerializationContext, IProgressMonitor)} * @return the URI for the given reference string or null * @since 3.2 */ public URI getURI(String referenceString) { return null; } /** * Return the name of the project that is the target of the given * reference string or null if this capability does not * support parsing of reference strings. * @see #getURI(String) * @param referenceString reference string obtained from * {@link #asReference(IProject[], ProjectSetSerializationContext, IProgressMonitor)} * @return the name of the project that is the target of the given * reference string or null * @since 3.2 */ public String getProject(String referenceString) { return null; } /** * Convert the given URI and projectName to a reference string that can be * passed to the * {@link #addToWorkspace(String[], ProjectSetSerializationContext, IProgressMonitor)} * method. The scheme of the provided URI must match the scheme of the * repository provider type from which this capability was obtained. *

* Since 3.7 SCM URIs are also accepted. *

*

* The default implementation returns null. Subclasses may * override. *

* * @see #SCHEME_SCM * @param uri * the URI that identifies the location of the project in the * repository. * @param projectName * the name of the project to use. If null, use a * project name from the provided SCM URI. If the URI does not * contain the project name the last segment of the URI's path is * used. If this fails, null is returned. * @return the reference string representing a project that can be loaded * into the workspace or null, if the URI and name * cannot be translated into a reference string * @since 3.2 */ public String asReference(URI uri, String projectName) { return null; } }