blob: 5412e8c4ff8b65765a8d2532816b070523d570a2 [file] [log] [blame]
/*
* Copyright (c) 2015, 2016, 2018 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.setup.internal.core.util;
import org.eclipse.oomph.base.Annotation;
import org.eclipse.oomph.base.BaseAnnotationConstants;
import org.eclipse.oomph.base.BaseFactory;
import org.eclipse.oomph.base.BasePackage;
import org.eclipse.oomph.base.ModelElement;
import org.eclipse.oomph.base.util.ArchiveResourceImpl;
import org.eclipse.oomph.base.util.BaseResource;
import org.eclipse.oomph.base.util.BaseResourceFactoryImpl;
import org.eclipse.oomph.base.util.BaseResourceImpl;
import org.eclipse.oomph.base.util.BaseUtil;
import org.eclipse.oomph.internal.setup.SetupProperties;
import org.eclipse.oomph.p2.P2Factory;
import org.eclipse.oomph.p2.P2Package;
import org.eclipse.oomph.p2.Repository;
import org.eclipse.oomph.p2.Requirement;
import org.eclipse.oomph.p2.core.P2Util;
import org.eclipse.oomph.preferences.impl.PreferencesURIHandlerImpl;
import org.eclipse.oomph.preferences.util.PreferencesUtil;
import org.eclipse.oomph.setup.AnnotationConstants;
import org.eclipse.oomph.setup.Macro;
import org.eclipse.oomph.setup.Parameter;
import org.eclipse.oomph.setup.Scope;
import org.eclipse.oomph.setup.SetupFactory;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.SetupCorePlugin;
import org.eclipse.oomph.setup.internal.core.util.ECFURIHandlerImpl.AuthorizationHandler;
import org.eclipse.oomph.setup.internal.core.util.ECFURIHandlerImpl.AuthorizationHandlerImpl;
import org.eclipse.oomph.setup.p2.P2Task;
import org.eclipse.oomph.setup.p2.SetupP2Factory;
import org.eclipse.oomph.setup.p2.util.MarketPlaceListing;
import org.eclipse.oomph.setup.util.SetupUtil;
import org.eclipse.oomph.util.IORuntimeException;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.OS;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.ReflectUtil;
import org.eclipse.oomph.util.ReflectUtil.ReflectionException;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.EPackageImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.URIHandler;
import org.eclipse.emf.ecore.resource.impl.ArchiveURIHandlerImpl;
import org.eclipse.emf.ecore.resource.impl.FileURIHandlerImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceFactoryRegistryImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIException;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.UIServices;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author Eike Stepper
*/
public final class SetupCoreUtil
{
public static final String OOMPH_NAMESPACE = "org.eclipse.oomph";
public static final String STATS_URI = "http://download.eclipse.org/stats/oomph";
public static final AuthorizationHandler AUTHORIZATION_HANDLER;
public static Resource.Factory.Registry RESOURCE_FACTORY_REGISTRY = new ResourceSetImpl().getResourceFactoryRegistry();
static
{
Resource.Factory factory = new BaseResourceFactoryImpl();
Map<String, Object> extensionToFactoryMap = RESOURCE_FACTORY_REGISTRY.getExtensionToFactoryMap();
extensionToFactoryMap.put("xmi", factory);
extensionToFactoryMap.put("setup", factory);
extensionToFactoryMap.put("targlet", factory);
extensionToFactoryMap.put("def", factory);
extensionToFactoryMap.put("ext", factory);
extensionToFactoryMap.put("ecore", new EcoreResourceFactoryImpl());
}
public static URIConverter URI_CONVERTER = new ResourceSetImpl().getURIConverter();
static
{
configureURIHandlers(URI_CONVERTER);
}
private static final boolean SKIP_STATS = PropertiesUtil.isProperty(SetupProperties.PROP_SETUP_STATS_SKIP);
private static volatile Map<URI, URI> archiveRedirections;
private static volatile String archiveExpectedETag;
static
{
IProvisioningAgent agent = P2Util.getCurrentProvisioningAgent();
UIServices uiServices = (UIServices)agent.getService(UIServices.SERVICE_NAME);
ISecurePreferences root = PreferencesUtil.getSecurePreferences();
ISecurePreferences securePreferences = root.node(OOMPH_NAMESPACE).node("hosts");
AUTHORIZATION_HANDLER = new AuthorizationHandlerImpl(uiServices, securePreferences);
}
private static Resource.Factory MARKET_PLACE_LISTING_RESOURCE_FACTORY = new BaseResourceFactoryImpl()
{
@Override
protected BaseResource basicCreateResource(URI uri)
{
return new BaseResourceImpl(uri)
{
@Override
public void load(Map<?, ?> options) throws IOException
{
Macro macro = SetupFactory.eINSTANCE.createMacro();
MarketPlaceListing marketPlaceListing = MarketPlaceListing.getMarketPlaceListing(uri, getURIConverter());
if (marketPlaceListing != null)
{
IOException exception = marketPlaceListing.getException();
if (exception != null)
{
getErrors().add(new XMIException(exception));
}
else
{
macro.setLabel(marketPlaceListing.getLabel());
macro.setName(marketPlaceListing.getListing().lastSegment());
macro.setDescription("<a href='" + marketPlaceListing.getListing() + "?'>" + marketPlaceListing.getLabel() + "</a>");
P2Task p2Task = SetupP2Factory.eINSTANCE.createP2Task();
p2Task.setLabel(marketPlaceListing.getLabel());
EList<Parameter> parameters = macro.getParameters();
List<Requirement> requirements = marketPlaceListing.getRequirements();
for (Requirement requirement : requirements)
{
if (!MarketPlaceListing.isRequired(requirement))
{
String name = requirement.getName();
if (name.endsWith(Requirement.FEATURE_SUFFIX))
{
name = name.substring(0, name.length() - Requirement.FEATURE_SUFFIX.length());
}
name += ".enabled";
Parameter parameter = SetupFactory.eINSTANCE.createParameter();
parameter.setName(name);
parameter.setDescription("Whether to include '" + name + "' in the installation");
parameter.setDefaultValue(MarketPlaceListing.isSelected(requirement) ? "true" : "false");
parameters.add(parameter);
Annotation featureSubstitutionAnnotation = BaseFactory.eINSTANCE.createAnnotation(AnnotationConstants.ANNOTATION_FEATURE_SUBSTITUTION);
featureSubstitutionAnnotation.getDetails().put(P2Package.Literals.REQUIREMENT__OPTIONAL.getName(), "${" + name + "|not}");
featureSubstitutionAnnotation.getDetails().put(P2Package.Literals.REQUIREMENT__GREEDY.getName(), "${" + name + "}");
requirement.getAnnotations().add(featureSubstitutionAnnotation);
}
}
p2Task.getRequirements().addAll(requirements);
URI updateSite = marketPlaceListing.getUpdateSite();
if (updateSite != null)
{
Repository repository = P2Factory.eINSTANCE.createRepository(marketPlaceListing.getUpdateSite().toString());
p2Task.getRepositories().add(repository);
}
macro.getSetupTasks().add(p2Task);
}
}
getContents().add(macro);
}
};
}
};
private SetupCoreUtil()
{
}
public static String getLabel(Scope scope)
{
if (scope == null)
{
return "";
}
String label = scope.getLabel();
if (StringUtil.isEmpty(label))
{
label = StringUtil.safe(scope.getName());
}
return label;
}
public static ResourceSet createResourceSet()
{
ResourceSet resourceSet = new ResourceSetImpl();
configureResourceSet(resourceSet);
return resourceSet;
}
public static void configureResourceSet(final ResourceSet resourceSet)
{
configureResourceSet(resourceSet, true);
}
private static void configureResourceSet(final ResourceSet resourceSet, boolean configureURIMappings)
{
final Resource.Factory.Registry resourceFactoryRegistry = resourceSet.getResourceFactoryRegistry();
final Resource.Factory.Registry specializedResourceFactoryRegistry = new ResourceFactoryRegistryImpl()
{
@Override
protected Resource.Factory delegatedGetFactory(URI uri, String contentTypeIdentifier)
{
if (MarketPlaceListing.isMarketPlaceListing(uri))
{
return MARKET_PLACE_LISTING_RESOURCE_FACTORY;
}
return resourceFactoryRegistry.getFactory(uri, contentTypeIdentifier);
}
@Override
protected URIConverter getURIConverter()
{
return resourceSet.getURIConverter();
}
@Override
protected Map<?, ?> getContentDescriptionOptions()
{
Map<?, ?> contentDescriptionOptions = super.getContentDescriptionOptions();
Map<Object, Object> result = new HashMap<Object, Object>(contentDescriptionOptions);
result.putAll(resourceSet.getLoadOptions());
return result;
}
};
specializedResourceFactoryRegistry.getExtensionToFactoryMap().putAll(RESOURCE_FACTORY_REGISTRY.getExtensionToFactoryMap());
resourceSet.setResourceFactoryRegistry(specializedResourceFactoryRegistry);
URIConverter uriConverter = resourceSet.getURIConverter();
Map<URI, URI> uriMap = uriConverter.getURIMap();
configureURIHandlers(uriConverter);
class ModelResourceSet extends ResourceSetImpl
{
private Map<Resource, Resource> redirectedResources = new HashMap<Resource, Resource>();
public ModelResourceSet()
{
uriConverter = resourceSet.getURIConverter();
packageRegistry = resourceSet.getPackageRegistry();
resourceFactoryRegistry = specializedResourceFactoryRegistry;
loadOptions = resourceSet.getLoadOptions();
}
@Override
protected Resource delegatedGetResource(URI uri, boolean loadOnDemand)
{
Resource result = super.delegatedGetResource(uri, loadOnDemand);
if (result == null)
{
result = super.delegatedGetResource(uriConverter.normalize(uri), loadOnDemand);
}
return result;
}
@Override
public Resource getResource(URI uri, boolean loadOnDemand)
{
try
{
Resource resource = super.getResource(uri, true);
Resource redirectedResource = redirectedResources.get(resource);
if (redirectedResource != null)
{
return redirectedResource;
}
if (resource.getResourceSet() == this)
{
synchronized (resourceSet)
{
resourceSet.getResources().add(resource);
}
}
return resource;
}
catch (RuntimeException throwable)
{
Resource resource = super.getResource(uri, false);
if (resource.getResourceSet() == this)
{
synchronized (resourceSet)
{
resourceSet.getResources().add(resource);
}
}
if (loadOnDemand)
{
throw throwable;
}
else
{
return null;
}
}
}
@Override
protected void demandLoad(Resource resource) throws IOException
{
super.demandLoad(resource);
EPackage ePackage = (EPackage)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.Literals.EPACKAGE);
if (ePackage != null)
{
String nsURI = ePackage.getNsURI();
EPackage redirectedEPackage = packageRegistry.getEPackage(nsURI);
for (EClassifier eClassifier : ePackage.getEClassifiers())
{
String instanceClassName = eClassifier.getInstanceClassName();
if (instanceClassName != null)
{
if (eClassifier instanceof EDataType)
{
eClassifier.setInstanceClass(String.class);
}
else
{
eClassifier.setInstanceClassName(null);
eClassifier.setInstanceClass(null);
}
}
}
if (redirectedEPackage != null)
{
redirectedResources.put(resource, redirectedEPackage.eResource());
packageRegistry.put(resource.getURI().toString(), redirectedEPackage);
packageRegistry.put(uriConverter.normalize(resource.getURI()).toString(), redirectedEPackage);
}
else
{
packageRegistry.put(resource.getURI().toString(), ePackage);
packageRegistry.put(uriConverter.normalize(resource.getURI()).toString(), ePackage);
((EPackageImpl)ePackage).freeze();
}
}
}
}
final ModelResourceSet modelResourceSet = new ModelResourceSet();
new ResourceSetImpl.MappedResourceLocator((ResourceSetImpl)resourceSet)
{
@Override
public Resource getResource(URI uri, boolean loadOnDemand)
{
if (BaseUtil.isBogusURI(uri))
{
return null;
}
if ("ecore".equals(uri.fileExtension()))
{
Resource resource = null;
synchronized (resourceSet)
{
resource = super.getResource(uri, false);
if (resource != null)
{
if (!resource.isLoaded())
{
demandLoadHelper(resource);
}
return resource;
}
resource = modelResourceSet.getResource(uri, loadOnDemand);
if (resource != null)
{
return resource;
}
}
}
return super.getResource(uri, loadOnDemand);
}
};
if (configureURIMappings)
{
uriMap.put(SetupContext.INDEX_ROOT_URI, SetupContext.INDEX_ROOT_LOCATION_URI);
configureRedirections(uriMap);
if (!SetupUtil.SETUP_ARCHIVER_APPLICATION)
{
handleArchiveRedirection(uriConverter);
}
}
}
private static final Map<URI, WeakReference<byte[]>> ZIPS = Collections.synchronizedMap(new HashMap<URI, WeakReference<byte[]>>());
private static void configureURIHandlers(URIConverter uriConverter)
{
EList<URIHandler> uriHandlers = uriConverter.getURIHandlers();
for (int i = 0, size = uriHandlers.size(); i < size; ++i)
{
URIHandler uriHandler = uriHandlers.get(i);
if (uriHandler instanceof FileURIHandlerImpl)
{
uriHandlers.set(i, new FileURIHandlerImpl()
{
@Override
public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException
{
if (!"zip".equals(uri.fileExtension()))
{
return super.createInputStream(uri, options);
}
// Look for a cached version first.
WeakReference<byte[]> bytesReference = ZIPS.get(uri);
if (bytesReference != null)
{
byte[] bytes = bytesReference.get();
if (bytes != null)
{
return new ByteArrayInputStream(bytes);
}
}
// Otherwise read the entire input stream into memory and cache it.
InputStream in = super.createInputStream(uri, options);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try
{
IOUtil.copy(in, out);
byte[] bytes = out.toByteArray();
ZIPS.put(uri, new WeakReference<byte[]>(bytes));
return new ByteArrayInputStream(bytes);
}
catch (IORuntimeException ex)
{
throw (IOException)ex.getCause();
}
finally
{
IOUtil.close(in);
}
}
});
}
else if (uriHandler instanceof ArchiveURIHandlerImpl)
{
uriHandlers.set(i, new ArchiveURIHandlerImpl()
{
@Override
public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException
{
// Look for a cached version first.
WeakReference<byte[]> bytesReference = ZIPS.get(uri);
if (bytesReference != null)
{
byte[] bytes = bytesReference.get();
if (bytes != null)
{
return new ByteArrayInputStream(bytes);
}
}
String authority = uri.authority();
URI archiveURI = URI.createURI(authority.substring(0, authority.length() - 1));
URI baseURI = uri.trimSegments(uri.segmentCount());
byte[] result = null;
InputStream inputStream = getURIConverter(options).createInputStream(archiveURI, options);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
for (ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntry != null; zipEntry = zipInputStream.getNextEntry())
{
String name = zipEntry.getName();
URI entryURI = URI.createURI(name).resolve(baseURI);
ByteArrayOutputStream output = new ByteArrayOutputStream();
try
{
IOUtil.copy(zipInputStream, output);
byte[] bytes = output.toByteArray();
ZIPS.put(entryURI, new WeakReference<byte[]>(bytes));
if (entryURI.equals(uri))
{
result = bytes;
}
}
catch (IORuntimeException ex)
{
throw (IOException)ex.getCause();
}
}
zipInputStream.close();
if (result == null)
{
throw new FileNotFoundException(uri.toString());
}
return new ByteArrayInputStream(result);
}
});
}
}
uriHandlers.add(4, new UserURIHandlerImpl());
uriHandlers.add(5, new SelfProductCatalogURIHandlerImpl());
uriHandlers.add(6, new PreferencesURIHandlerImpl());
uriHandlers.add(7, new ECFURIHandlerImpl(AUTHORIZATION_HANDLER));
}
static void configureRedirections(Map<URI, URI> uriMap)
{
Properties properties = System.getProperties();
Map<Object, Object> safeProperties;
synchronized (properties)
{
safeProperties = new HashMap<Object, Object>(properties);
}
for (Map.Entry<Object, Object> entry : safeProperties.entrySet())
{
Object key = entry.getKey();
if (key instanceof String)
{
if (((String)key).startsWith(SetupProperties.PROP_REDIRECTION_BASE))
{
String[] mapping = ((String)entry.getValue()).split("->");
if (mapping.length == 1)
{
URI sourceURI = URI.createURI(mapping[0]);
uriMap.remove(sourceURI);
}
else if (mapping.length == 2)
{
URI sourceURI = URI.createURI(mapping[0]);
URI targetURI = URI.createURI(mapping[1].replace("\\", "/"));
// Only include the mapping if the target exists.
// For example, we often include a redirection of the remote setup to the local git clone in an installed IDE,
// but if that clone hasn't been cloned yet, we want to continue to use the remote version.
//
if (targetURI.isFile())
{
// If the file is a relative path, interpret it as relative to the root folder of the installation.
if (targetURI.isRelative())
{
targetURI = targetURI.resolve(SetupContext.PRODUCT_LOCATION.trimSegments(OS.INSTANCE.isMac() ? 2 : 0).appendSegment(""));
}
File file = new File(targetURI.toFileString());
if (!file.exists())
{
continue;
}
}
uriMap.put(sourceURI, targetURI);
}
}
}
}
}
private static void handleArchiveRedirection(final URIConverter uriConverter)
{
URI indexSetupArchiveLocation = uriConverter.normalize(SetupContext.INDEX_SETUP_ARCHIVE_LOCATION_URI);
if (archiveExpectedETag == null || !archiveExpectedETag.equals(ECFURIHandlerImpl.getExpectedETag(indexSetupArchiveLocation)))
{
Map<Object, Object> options = new HashMap<Object, Object>();
options.put(ECFURIHandlerImpl.OPTION_CACHE_HANDLING, ECFURIHandlerImpl.CacheHandling.CACHE_WITH_ETAG_CHECKING);
final Map<URI, URI> redirections = new HashMap<URI, URI>();
Resource archiveResource = new ArchiveResourceImpl(SetupContext.INDEX_SETUP_ARCHIVE_LOCATION_URI, uriConverter)
{
@Override
protected void handle(EMap<String, String> details, URI uri, URI archiveEntry)
{
redirections.put(uri, archiveEntry);
}
};
try
{
archiveResource.load(options);
}
catch (IOException ex)
{
SetupCorePlugin.INSTANCE.log(ex, IStatus.WARNING);
ECFURIHandlerImpl.setExpectedETag(indexSetupArchiveLocation, "Failed");
}
archiveRedirections = redirections;
archiveExpectedETag = ECFURIHandlerImpl.getExpectedETag(indexSetupArchiveLocation);
}
Map<URI, URI> uriMap = uriConverter.getURIMap();
for (Map.Entry<URI, URI> entry : archiveRedirections.entrySet())
{
// Use the archive redirection only if the URI isn't already redirected elsewhere.
URI sourceURI = entry.getKey();
if (sourceURI.equals(uriConverter.normalize(sourceURI)))
{
URI targetURI = entry.getValue();
uriMap.put(sourceURI, targetURI);
}
}
}
public static <T> void reorder(EList<T> values, DependencyProvider<T> dependencyProvider)
{
for (int i = 0, size = values.size(), count = 0; i < size; ++i)
{
T value = values.get(i);
if (count == size)
{
SetupCorePlugin.INSTANCE.log("Circular dependencies " + value, IStatus.WARNING);
return;
}
boolean changed = false;
// TODO Consider basing this on a provider that just returns a boolean based on "does v1 depend on v2".
for (T dependency : dependencyProvider.getDependencies(value))
{
int index = values.indexOf(dependency);
if (index > i)
{
values.move(i, index);
changed = true;
}
}
if (changed)
{
--i;
++count;
}
else
{
count = 0;
}
}
}
public static void migrate(Resource resource, Collection<EObject> result)
{
new Migrator(resource).migrate(result);
}
public static void sendStats(boolean success, Scope scope, OS os)
{
if (SKIP_STATS || scope == null)
{
return;
}
Resource resource = scope.eResource();
if (resource != null)
{
ResourceSet resourceSet = resource.getResourceSet();
if (resourceSet != null)
{
URI statusURI = getStatsURI(success, scope);
if (statusURI != null)
{
if (os != null)
{
statusURI = statusURI.appendSegment(os.getOsgiOS());
statusURI = statusURI.appendSegment(os.getOsgiWS());
statusURI = statusURI.appendSegment(os.getOsgiArch());
}
resourceSet.getURIConverter().exists(statusURI, null);
}
}
}
}
public static URI getStatsURI(boolean success, Scope scope)
{
String prefix = getStatsPrefix(scope);
if (prefix == null)
{
return null;
}
URI statusURI = null;
List<String> names = new ArrayList<String>();
for (Scope x = scope; x != null; x = x.getParentScope())
{
if (SetupContext.isUserScheme(EcoreUtil.getURI(x).scheme()))
{
return null;
}
if (statusURI == null)
{
Annotation annotation = x.getAnnotation(AnnotationConstants.ANNOTATION_STATS_SENDING);
if (annotation != null)
{
String uri = annotation.getDetails().get(AnnotationConstants.KEY_URI);
if (StringUtil.isEmpty(uri))
{
return null;
}
statusURI = URI.createURI(uri);
}
}
names.add(0, URI.encodeSegment(x.getName(), false));
}
if (statusURI != null)
{
statusURI = statusURI.appendSegment(prefix).appendSegment(success ? "success" : "failure");
for (String name : names)
{
statusURI = statusURI.appendSegment(name);
}
}
return statusURI;
}
private static String getStatsPrefix(Scope scope)
{
switch (scope.getType())
{
case PRODUCT_VERSION:
{
return "product";
}
case STREAM:
{
return "project";
}
}
return null;
}
/**
* @author Eike Stepper
*/
public interface DependencyProvider<T>
{
Collection<? extends T> getDependencies(T value);
}
/**
* @author Ed Merks
*/
public static class Migrator
{
/**
* @author Ed Merks
*/
private static class MigrationCopier extends EcoreUtil.Copier
{
private static final long serialVersionUID = 1L;
private EPackage.Registry packageRegistry;
public MigrationCopier(EPackage.Registry packageRegistry)
{
// super(true, false);
this.packageRegistry = packageRegistry;
}
public EObject copyAsProxy(EObject eObject)
{
EObject copiedEObject = createCopy(eObject);
if (copiedEObject != null)
{
put(eObject, copiedEObject);
((InternalEObject)copiedEObject).eSetProxyURI(URI.createURI("bogus:/" + BaseUtil.getRootURI(eObject)));
}
return copiedEObject;
}
@Override
protected EClass getTarget(EClass eClass)
{
return getEClass(eClass, "name", "nsURI");
}
protected EClass getEClass(ENamedElement eNamedElement, String nameAnnotation, String nsURIAnnotation)
{
String nsURIs = getAnnotation(eNamedElement, nsURIAnnotation);
if (nsURIs == null)
{
for (EObject eContainer = eNamedElement.eContainer(); eContainer != null; eContainer = eContainer.eContainer())
{
if (eContainer instanceof EPackage)
{
nsURIs = getAnnotation((EPackage)eContainer, "nsURIs");
}
}
}
if (nsURIs == null)
{
throw new IllegalStateException("Cannot find package URIs for " + EcoreUtil.getURI(eNamedElement));
}
String name = getAnnotation(eNamedElement, nameAnnotation);
if (name == null)
{
name = eNamedElement.getName();
}
for (String nsURI : nsURIs.split(" "))
{
EPackage ePackage = packageRegistry.getEPackage(nsURI);
if (ePackage != null)
{
EClassifier eClassifier = ePackage.getEClassifier(name);
if (eClassifier instanceof EClass)
{
return (EClass)eClassifier;
}
}
}
throw new IllegalStateException("Cannot find class '" + name + "' for to " + EcoreUtil.getURI(eNamedElement));
}
@Override
protected EStructuralFeature getTarget(EStructuralFeature eStructuralFeature)
{
String name = getAnnotation(eStructuralFeature, "name");
if (name == null)
{
name = eStructuralFeature.getName();
}
EStructuralFeature result = getTarget(eStructuralFeature.getEContainingClass()).getEStructuralFeature(name);
if (result != null)
{
return result;
}
throw new IllegalStateException("Cannot find feature corresponding to " + EcoreUtil.getURI(eStructuralFeature));
}
@Override
protected Setting getTarget(EStructuralFeature eStructuralFeature, EObject eObject, EObject copyEObject)
{
String name = getAnnotation(eStructuralFeature, "name");
// The blank name is used to discard features which are not intended to be migrated.
if ("".equals(name))
{
return null;
}
if (name == null)
{
name = eStructuralFeature.getName();
}
String targetName = getAnnotation(eStructuralFeature, "targetName");
if (targetName != null)
{
EClass targetEClass = getEClass(eStructuralFeature, "targetName", "targetNsURI");
EClass eClass = copyEObject.eClass();
for (EReference eReference : eClass.getEAllContainments())
{
EClass eReferenceType = eReference.getEReferenceType();
if (eReferenceType == targetEClass)
{
EStructuralFeature.Setting setting = demandCreateContainer(copyEObject, targetEClass, eReferenceType, name, eStructuralFeature, eReference);
if (setting != null && targetEClass == BasePackage.Literals.ANNOTATION)
{
// Handle mappings to annotations in a special way to record the original as a details entry.
Annotation annotation = (Annotation)setting.getEObject();
annotation.setSource(BaseAnnotationConstants.ANNOTATION_SOURCE);
@SuppressWarnings("unchecked")
Map.Entry<String, String> stringToStringMapEntry = (Map.Entry<String, String>)BaseFactory.eINSTANCE
.create(BasePackage.Literals.STRING_TO_STRING_MAP_ENTRY);
InternalEObject mapEntry = (InternalEObject)stringToStringMapEntry;
mapEntry.eSet(BasePackage.Literals.STRING_TO_STRING_MAP_ENTRY__KEY, EcoreUtil.getURI(eStructuralFeature).toString());
annotation.getDetails().add(stringToStringMapEntry);
return mapEntry.eSetting(BasePackage.Literals.STRING_TO_STRING_MAP_ENTRY__VALUE);
}
return setting;
}
}
}
EStructuralFeature.Setting setting = demandCreateContainer(copyEObject, name, eStructuralFeature);
if (setting != null)
{
return setting;
}
throw new IllegalStateException("Cannot find feature corresponding to " + EcoreUtil.getURI(eStructuralFeature));
}
@Override
protected void copyAttribute(EAttribute eAttribute, EObject eObject, EObject copyEObject)
{
// For attributes that are targeting an Annotation to be copied even if eIsSet is false.
String targetName = getAnnotation(eAttribute, "targetName");
if (targetName != null)
{
EClass targetEClass = getEClass(eAttribute, "targetName", "targetNsURI");
if (targetEClass == BasePackage.Literals.ANNOTATION)
{
EStructuralFeature.Setting setting = getTarget(eAttribute, eObject, copyEObject);
if (setting != null)
{
copyAttributeValue(eAttribute, eObject, eObject.eGet(eAttribute), setting);
return;
}
}
}
super.copyAttribute(eAttribute, eObject, copyEObject);
}
protected EStructuralFeature.Setting demandCreateContainer(final EObject copyEObject, String name, EStructuralFeature eStructuralFeature)
{
return demandCreateContainer(new HashSet<EClass>(), copyEObject, name, eStructuralFeature);
}
protected EStructuralFeature.Setting demandCreateContainer(Set<EClass> tried, final EObject copyEObject, String name,
EStructuralFeature eStructuralFeature)
{
EClass targetEClass = copyEObject.eClass();
if (!tried.add(targetEClass))
{
return null;
}
EStructuralFeature targetEStructuralFeature = targetEClass.getEStructuralFeature(name);
if (targetEStructuralFeature != null)
{
if (targetEStructuralFeature instanceof EReference && eStructuralFeature instanceof EReference)
{
EClass actualTargetEClass = ((EReference)targetEStructuralFeature).getEReferenceType();
EClass expectedTargetEClass = getTarget(((EReference)eStructuralFeature).getEReferenceType());
if (actualTargetEClass != expectedTargetEClass)
{
// Look for a containment feature that can hold the actual target instance.
for (EReference eReference : targetEClass.getEAllContainments())
{
EClass eReferenceType = eReference.getEReferenceType();
if (eReferenceType.isSuperTypeOf(actualTargetEClass))
{
// Look for containment features of that reference's type to find one that can hold the expected target instance.
for (EReference childEReference : eReferenceType.getEAllContainments())
{
if (childEReference.getEReferenceType().isSuperTypeOf(expectedTargetEClass))
{
// Create a container and a setting for that child reference.
return demandCreateContainer(copyEObject, actualTargetEClass, eReference, childEReference, true);
}
}
}
}
throw new IllegalStateException("Couldn't find a matching feature for " + EcoreUtil.getURI(eStructuralFeature));
}
}
return ((InternalEObject)copyEObject).eSetting(targetEStructuralFeature);
}
EClass targetEContainingClass = getTarget(eStructuralFeature.getEContainingClass());
if (targetEContainingClass != null)
{
for (EReference eReference : targetEClass.getEAllContainments())
{
EClass eReferenceType = eReference.getEReferenceType();
if (targetEContainingClass.isSuperTypeOf(eReferenceType))
{
EStructuralFeature.Setting setting = demandCreateContainer(copyEObject, targetEContainingClass, eReferenceType, name, eStructuralFeature,
eReference);
if (setting != null)
{
return setting;
}
}
}
// Try to create intermediate containers...
for (final EReference eReference : targetEClass.getEAllContainments())
{
EClass eReferenceEType = eReference.getEReferenceType();
if (!eReferenceEType.isAbstract())
{
EObject targetContainer;
Runnable addToContainer = null;
Object targetValue = copyEObject.eGet(eReference);
if (eReference.isMany())
{
@SuppressWarnings("unchecked")
final List<EObject> targetValues = (List<EObject>)targetValue;
if (targetValues.isEmpty())
{
final EObject demandCreatedTargetContainer = EcoreUtil.create(eReferenceEType);
targetContainer = demandCreatedTargetContainer;
addToContainer = new Runnable()
{
public void run()
{
targetValues.add(demandCreatedTargetContainer);
}
};
}
else
{
targetContainer = targetValues.get(0);
}
}
else if (targetValue == null)
{
final EObject demandCreatedTargetContainer = EcoreUtil.create(eReferenceEType);
targetContainer = demandCreatedTargetContainer;
addToContainer = new Runnable()
{
public void run()
{
copyEObject.eSet(eReference, demandCreatedTargetContainer);
}
};
}
else
{
targetContainer = (EObject)targetValue;
}
// If we can create a setting, add the container to really use it and return that setting.
EStructuralFeature.Setting setting = demandCreateContainer(tried, targetContainer, name, eStructuralFeature);
if (setting != null)
{
if (addToContainer != null)
{
addToContainer.run();
}
return setting;
}
}
}
}
return null;
}
protected EStructuralFeature.Setting demandCreateContainer(EObject copyEObject, EClass targetEClass, EClass eReferenceType, String name,
EStructuralFeature eStructuralFeature, EReference eReference)
{
if (targetEClass.isSuperTypeOf(eReferenceType))
{
EStructuralFeature targetEStructuralFeature = targetEClass.getEStructuralFeature(name);
if (targetEStructuralFeature == null)
{
throw new IllegalStateException("Cannot find feature corresponding to " + EcoreUtil.getURI(eStructuralFeature));
}
return demandCreateContainer(copyEObject, eReferenceType, eReference, targetEStructuralFeature, false);
}
return null;
}
private EStructuralFeature.Setting demandCreateContainer(EObject copyEObject, EClass eReferenceType, EReference eReference,
EStructuralFeature settingFeature, boolean additional)
{
EObject targetContainer;
Object targetValue = copyEObject.eGet(eReference);
if (eReference.isMany())
{
@SuppressWarnings("unchecked")
List<EObject> targetValues = (List<EObject>)targetValue;
if (additional || targetValues.isEmpty())
{
targetContainer = EcoreUtil.create(eReferenceType);
targetValues.add(targetContainer);
}
else
{
targetContainer = targetValues.get(0);
}
}
else if (additional || targetValue == null)
{
targetContainer = EcoreUtil.create(eReferenceType);
copyEObject.eSet(eReference, targetContainer);
}
else
{
targetContainer = (EObject)targetValue;
}
return ((InternalEObject)targetContainer).eSetting(settingFeature);
}
@Override
protected void copyAttributeValue(EAttribute eAttribute, EObject eObject, Object value, Setting setting)
{
EDataType eDataType = eAttribute.getEAttributeType();
EDataType targetEDataType = (EDataType)setting.getEStructuralFeature().getEType();
if (eDataType.getInstanceClass() != targetEDataType.getInstanceClass() || eDataType.getInstanceClass() == null)
{
if (eAttribute.isMany())
{
@SuppressWarnings("unchecked")
List<Object> values = (List<Object>)value;
@SuppressWarnings("unchecked")
List<Object> transformedValues = (List<Object>)setting.get(false);
for (Object object : values)
{
transformedValues.add(convert(eDataType, targetEDataType, object));
}
}
else
{
setting.set(convert(eDataType, targetEDataType, value));
}
}
else
{
super.copyAttributeValue(eAttribute, eObject, value, setting);
}
}
protected Object convert(EDataType sourceEDataType, EDataType targetEDataType, Object sourceValue)
{
return EcoreUtil.createFromString(targetEDataType, EcoreUtil.convertToString(sourceEDataType, sourceValue));
}
private String getAnnotation(EModelElement eModelElement, String key)
{
return EcoreUtil.getAnnotation(eModelElement, BaseAnnotationConstants.ANNOTATION_SOURCE, key);
}
}
private Resource resource;
public Migrator(Resource resource)
{
this.resource = resource;
}
public Collection<? extends EObject> migrate(Collection<EObject> result)
{
MigrationCopier migrationCopier = new MigrationCopier(resource.getResourceSet().getPackageRegistry());
Set<EObject> allContainedObjects = new HashSet<EObject>();
Set<EObject> allCrossReferencedObjects = new HashSet<EObject>();
for (Iterator<EObject> it = resource.getAllContents(); it.hasNext();)
{
EObject containedEObject = it.next();
allContainedObjects.add(containedEObject);
for (EObject eObject : containedEObject.eCrossReferences())
{
allCrossReferencedObjects.add(eObject);
if (eObject.eIsProxy())
{
migrationCopier.copy(eObject);
}
}
}
allCrossReferencedObjects.removeAll(allContainedObjects);
for (EObject eObject : allCrossReferencedObjects)
{
migrationCopier.copyAsProxy(eObject);
}
RuntimeException runtimeException = null;
try
{
migrationCopier.copyAll(resource.getContents());
}
catch (RuntimeException ex)
{
runtimeException = ex;
}
try
{
migrationCopier.copyReferences();
}
catch (RuntimeException ex)
{
if (runtimeException == null)
{
runtimeException = ex;
}
}
for (EObject eObject : resource.getContents())
{
EObject copiedEObject = migrationCopier.get(eObject);
if (copiedEObject != null)
{
result.add(copiedEObject);
}
}
if (runtimeException != null)
{
throw runtimeException;
}
Set<ModelElement> annotatedModelElements = new LinkedHashSet<ModelElement>();
for (TreeIterator<Object> it = EcoreUtil.getAllContents(result); it.hasNext();)
{
Object object = it.next();
if (object instanceof Annotation)
{
Annotation annotation = (Annotation)object;
if (BaseAnnotationConstants.ANNOTATION_SOURCE.equals(annotation.getSource()))
{
annotatedModelElements.add(annotation.getModelElement());
}
}
}
for (ModelElement modelElement : annotatedModelElements)
{
try
{
ReflectUtil.invokeMethod(ReflectUtil.getMethod(modelElement.getClass(), "eMigrate"), modelElement);
}
catch (ReflectionException exception)
{
// Ignore.
}
catch (IllegalArgumentException ex)
{
throw new RuntimeException(ex);
}
}
return result;
}
}
public static URI getEclipseBrandingImage()
{
return URI.createPlatformPluginURI("org.eclipse.oomph.setup.ui/icons/committers.png", true);
}
public static URI getBrandingImageURI(Scope scope)
{
URI imageURI = null;
if (scope != null)
{
Annotation annotation = scope.getAnnotation(AnnotationConstants.ANNOTATION_BRANDING_INFO);
if (annotation == null)
{
return getBrandingImageURI(scope.getParentScope());
}
String detail = annotation.getDetails().get(AnnotationConstants.KEY_IMAGE_URI);
if (detail == null)
{
return getBrandingImageURI(scope.getParentScope());
}
imageURI = URI.createURI(detail);
}
if (imageURI == null)
{
imageURI = getEclipseBrandingImage();
}
return imageURI;
}
}