blob: 3b3781c70d112d378186d87adb281b9965897433 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 E.D.Willink and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*
* The standalone functionality is heavily influenced by org.eclipse.emf.mwe.utils.StandaloneSetup.
*******************************************************************************/
package org.eclipse.ocl.examples.domain.utilities;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.WeakHashMap;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.SingletonAdapterImpl;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
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.impl.ResourceImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.examples.common.utils.TracingOption;
import org.eclipse.ocl.examples.domain.compatibility.EMF_2_9;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* StandaloneProjectMap and {@link ProjectMap} provide facilities to assist in
* preparing the {@link URIConverter}, the {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry}, and the
* and URIResourceMap of a {@link ResourceSet} and the global and
* {@link EcorePlugin#getPlatformResourceMap()} and
* {@link EcorePlugin#getEPackageNsURIToGenModelLocationMap} to support
* arbitrary and compatible use of dynamically loaded resources such as
* <tt>platform:/plugin</tt> and <tt>platform:/resource</tt> and
* generated EPackages such as registered namespace URIs in both plugin and standalone
* environments.
* <p>
* StandaloneProjectMap supports only standalone usage and so is free of
* dependencies on the Eclipse platform. ProjectMap extends StandaloneProjectMap
* to provide polymorphic standalone and plugin environments.
* <p>
* As a result, when the current file context is my.project/model/MyModel.ecore,
* and when the classpath contains only the JAR version of Ecore, referencing a
* resource as any or all of
* <ul>
* <li>http://www.eclipse.org/emf/2002/Ecore</li>
* <li>platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore</li>
* <li>platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore</li>
* <li>../../org.eclipse.emf.ecore/model/Ecore.ecore</li>
* </ul>
* results in the same Resource being returned by {@link
* ResourceSet#getResource(URI, boolean)}.
* <p>
* If the classpath contains distinct imported project and JAR versions of
* Ecore, referencing
* <ul>
* <li>http://www.eclipse.org/emf/2002/Ecore</li>
* </ul>
* returns the generated EPackage from the JAR plugin version while referencing
* <ul>
* <li>platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore</li>
* <li>platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore</li>
* <li>../../org.eclipse.emf.ecore/model/Ecore.ecore</li>
* </ul>
* returns a dynamically loaded imported project version.
* <p>
* A ProjectMap consists of a map from a project or bundle name to a location
* that is resolvable by the conventional Platform URL stream opening
* capabilities. Utility methods support export of the map to initialize the
* URIMap in a {@link URIConverter} and/or the
* {@link EcorePlugin#getPlatformResourceMap()}.
* <p>
* Minimal usage to configure <tt>aResourceSet</tt> is just <br>
* <tt>new ProjectMap().initializeResourceSet(aResourceSet);</tt> <br>
* or <tt>ProjectMap.getAdapter(aResourceSet);</tt> <br>
* Thereafter EMF accesses to projects and bundles should just work.
*
* <h4>Standalone Environment</h4>
*
* A resolvable location is a physical location such as
* <ul>
* <li>
* <tt>archive:file:/C:/Tools/Eclipse/3.7.1/plugins/org.antlr.runtime_3.2.0.v201101311130.jar!/</tt>
* </li>
* <li>
* <tt>file:/C:/GIT/org.eclipse.ocl/examples/org.eclipse.ocl.examples.common/</tt>
* </li>
* </ul>
* <p>
* {@link #getProjectDescriptors()} returns a map of project names and bundle names to a
* physical location which is established by searching the classpath for folders
* and JARs containing .project files. If a manifest is also found, the search
* has found a bundle and the Bundle-SymbolicName is read from the manifest.
* <p>
* {@link #initializePackageRegistry(ResourceSet)} populates a
* registration for each <tt>genPackages.ecorePackage</tt> referenced from a
* <tt>genmodel</tt> referenced from a
* <tt>org.eclipse.emf.ecore.generated_package</tt> defined in any
* <tt>plugin.xml</tt> found on the classpath. The three declarations ensure
* that when appropriate, each of the namespace URI (e.g.
* <tt>http://www.eclipse.org/emf/2002/Ecore</tt>), the project URI (e.g.
* <tt>platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore</tt>) and the
* plugin URI (e.g.
* <tt>platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore</tt>) resolve to
* the same Resource eliminating most opportunities for meta-model
* schizophrenia.
* <p>
* {@link #initializePlatformResourceMap(boolean)} populates
* {@link EcorePlugin#getPlatformResourceMap()} with a <i>project</i> to
* <tt>platform:/resource/<i>project</i></tt> entry for each project and a
* <i>bundle</i> to <tt>platform:/plugin/<i>bundle</i></tt> entry for each
* bundle.
* <p>
* {@link #initializeGenModelLocationMap(boolean)} exploits the classpath
* scan for plugins and projects to identify all plugin.xml files and populate
* the {@link EcorePlugin#getEPackageNsURIToGenModelLocationMap()} from the
* <tt>org.eclipse.emf.ecore.generated_package</tt> extension points in the same
* way as occurs automatically in a plugin environment.
* <p>
* {@link #initializeURIMap(ResourceSet)} installs a
* <tt>platform:/plugin/<i>project</i></tt> to
* <tt>platform:/resource/<i>project</i></tt> URI mapping for each project and a
* <tt>platform:/resource/<i>bundle</i></tt> to
* <tt>platform:/plugin/<i>bundle</i></tt> URI mapping for each bundle.
*
* <h4>Static Instances and Re-Use</h4>
*
* No static <tt>INSTANCE</tt> is provided because different class loaders or
* dynamic class path changes may result in stale content. Standalone
* applications are strongly advised to create their own static instance in a
* stable context and so avoid repeating the significant costs of a full class
* path search.
* <p>
* The {@link #getAdapter(ResourceSet)} method may be used to invoke
* {@link #initializeResourceSet(ResourceSet)} if not already invoked and to
* install the ProjectMap as a ResourceSet adapter allowing an invocation of
* {@link #findAdapter(ResourceSet)} to find it for subsequent re-use.
*
* <h4>Conflicts</h4>
*
* Use of both generated and dynamically loaded models normally results in
* obscure metamodel schizophrenia problems SyadaloneProjectMap allows a
* ResourceLoadStrategy to be independently specified for each resource to select
* just LoadGeneratedPackagSttrategye or just LoadDynamicResourceStrategy to force
* a particular usage. LoadFirstStrategy uses whichever is first used. LoadBothStrategy
* allows both generated and dynamically loaded resources to co-exist. When only one
* form is in use a conflicting access is resolved by an IConflictHandler. The default
* behaviour is the LoadFirstStrategy with conflicts silently resolved to whichever was
* first loaded.
* <p>
* Conflicts can only be diagnosed if the StandaloneProjectMap is aware of the conflict,
* so useGeneratedResource must be invoked if a GeneratedPackage is used without being
* loaded in the ResourceSet.
*
* <h4>Shared Model</h4>
*
* The classpath is analyzed to identify an IProjectDescriptor per project/bundle within which
* a plugin.xml is analyzed to identify generated_package extension points leading to a
* an IResourceDescriptor per genmodel and an IPackageDescriptor per nsURI/className within
* the IResorceDescriptor. There is usually only one nsURI per genmodel but the greater
* generality has to be accommodated. The foregoing constitute a shared model that can be re-used
* by multiple applications, so long as the classpath content is unchanged.
*
* <h4>Per-ResourceSet Model</h4>
*
* The actual state is maintained on a per-ResourceSet basis with an IResourceLoadStatus and
* one or more IPackageDescriptors for in use IResourceDescriptors and IPackageDescriptors. The
* IResourceLoadStatus is confugured with an IResourceLoadStrategy and an IConflictHandler.
*/
public class StandaloneProjectMap extends SingletonAdapterImpl
{
private static final String PLUGIN_ID = "org.eclipse.ocl.examples.domain";;
private static final Logger logger = Logger.getLogger(StandaloneProjectMap.class);
private static @Nullable Set<String> alreadyLogged = null;
public static final @NonNull TracingOption PROJECT_MAP_ADD_EPACKAGE = new TracingOption(PLUGIN_ID, "projectMap/addEPackage");
public static final @NonNull TracingOption PROJECT_MAP_ADD_GEN_MODEL = new TracingOption(PLUGIN_ID, "projectMap/addGenModel");
public static final @NonNull TracingOption PROJECT_MAP_ADD_GENERATED_PACKAGE = new TracingOption(PLUGIN_ID, "projectMap/addGeneratedPackage");
public static final @NonNull TracingOption PROJECT_MAP_ADD_URI_MAP = new TracingOption(PLUGIN_ID, "projectMap/addURIMap");
public static final @NonNull TracingOption PROJECT_MAP_CONFIGURE = new TracingOption(PLUGIN_ID, "projectMap/configure");
public static final @NonNull TracingOption PROJECT_MAP_GET = new TracingOption(PLUGIN_ID, "projectMap/get");
public static final @NonNull TracingOption PROJECT_MAP_INSTALL = new TracingOption(PLUGIN_ID, "projectMap/install");
public static final @NonNull TracingOption PROJECT_MAP_RESOLVE = new TracingOption(PLUGIN_ID, "projectMap/resolve");
{
// PROJECT_MAP_ADD_EPACKAGE.setState(true);
// PROJECT_MAP_ADD_GEN_MODEL.setState(true);
// PROJECT_MAP_ADD_GENERATED_PACKAGE.setState(true);
// PROJECT_MAP_ADD_URI_MAP.setState(true);
// PROJECT_MAP_CONFIGURE.setState(true);
// PROJECT_MAP_INSTALL.setState(true);
// PROJECT_MAP_RESOLVE.setState(true);
}
/**
* EPackageDescriptor is an EPackage.Descriptor that loads the appropriate EPackage to resolve a Namespace URI reference
* to a generated or dynamically loaded EPackage in accordance with the configured ResourceLoadStrategy.
*/
protected static class EPackageDescriptor implements EPackage.Descriptor
{
protected final @NonNull IPackageLoadStatus packageLoadStatus; // The PackageLoadStatus of the required package.
protected EPackageDescriptor(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage.Registry packageRegistry) {
this.packageLoadStatus = packageLoadStatus;
packageRegistry.put(getURI().toString(), this);
if (PROJECT_MAP_INSTALL.isActive()) {
PROJECT_MAP_INSTALL.println(toString());
}
}
public EFactory getEFactory() {
EPackage ePackage = getEPackage();
if (ePackage != null) {
return ePackage.getEFactoryInstance();
}
else {
return null;
}
}
public @Nullable EPackage getEPackage() {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
if (PROJECT_MAP_GET.isActive()) {
PROJECT_MAP_GET.println("Get " + getURI() + " with " + resourceLoadStrategy + " in " + DomainUtil.debugSimpleName(resourceLoadStatus.getPackageRegistry()));
}
return resourceLoadStrategy.getEPackage(packageLoadStatus);
}
public @NonNull URI getURI() {
return packageLoadStatus.getPackageDescriptor().getNsURI();
}
@Override
public @NonNull String toString() {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
return getURI() + " with " + resourceLoadStrategy;
}
public void uninstall(@NonNull EPackage.Registry packageRegistry) {
if (PROJECT_MAP_INSTALL.isActive()) {
PROJECT_MAP_INSTALL.println("" + toString());
}
packageRegistry.put(getURI().toString(), null);
}
}
/**
* An IResourceLoadStatus maintains the lazy load state of a resource associated with a genmodel
* identified by an IResourceDescriptor within a ResourceSet.
*/
public static interface IResourceLoadStatus
{
/**
* Configure the ResourceSet.URIResourceMap to resolve platform:/plugin and platform:/resource
* references to a pseudo resource that delegates to generated packages.
*/
void configureDelegatingResource();
/**
* Configure the EPackage.Registry to resolve namesapce URI references to the specified resource.
*/
void configureEPackageRegistry(@NonNull Resource resource);
/**
* Configure the ResourceSet.URIResourceMap to resolve platform:/plugin and platform:/resource
* references to the specified resource.
*/
void configureResourceSetURIResourceMap(@NonNull Resource resource);
/**
* Dispose of all facilities used by the IResourceLoadStatus, and remove all EPackageDescriptor entries.
*/
void dispose();
/**
* Return the EPackage to be used for a platform-resource/plugin URI after a namespace URI has already been loaded.
*/
@Nullable EPackage getConflictingDynamicResource(@NonNull EPackage ePackage);
/**
* Return the first loaded EPackage which may be part of a model or a Java generated EPackageinstance..
*/
@Nullable EPackage getFirstEPackage();
/**
* Return the package load status for the package identified by packageDescriptor
*/
@Nullable IPackageLoadStatus getPackageLoadStatus(@NonNull IPackageDescriptor packageDescriptor);
/**
* Return the descriptor for the resource.
*/
@NonNull IResourceDescriptor getResourceDescriptor();
/**
* Return the configured resource loading strategy.
*/
@NonNull IResourceLoadStrategy getResourceLoadStrategy();
/**
* Return the package registry maintained by this resource load status
*/
@NonNull EPackage.Registry getPackageRegistry();
/**
* Return the ResourceSet to which the resource logically belongs. (Note that generated EPackage may have
* a null actual ResourceSet, but a non-null logical ResourceSet.)
*/
@Nullable ResourceSet getResourceSet();
/**
* Load and return the EPackage appropriate to the platform resource or plugin resource using nsURI to identify
* a conflicting nsURI access,
*/
@Nullable Resource loadDynamicResource(@NonNull URI nsURI);
/**
* Load all the Java generated EPackage instances for the resource.
*/
void loadGeneratedPackages();
/**
* Define a new conflict handler.
*/
void setConflictHandler(@Nullable IConflictHandler conflictHandler);
/**
* Set true by Pivot2Ecore to inhibit auto-loading of newly added EPackages.
*/
void setGenerationInProgress(boolean isGenerating);
/**
* Define the resource once it has been loaded.
*/
void setResource(@NonNull Resource resource);
/**
* Define a new package load strategy.
*/
void setResourceLoadStrategy(@NonNull IResourceLoadStrategy resourceLoadStrategy);
/**
* Reset the status following notification that the model has been unloaded.
*/
void unloadedResource();
}
/**
* An IPackageLoadStatus maintains the lazy load state of a package within an EPackage.Registry
*/
public static interface IPackageLoadStatus
{
/**
* Configure the resourceSet EPackage.Registry for this package to resolve to the defined
* generated/loaded EPackage.
*/
void configureEPackageRegistry(@NonNull ResourceSet resourceSet);
/**
* Dispose of all facilities used by the PackageLoadStatus, and remove all EPackageDescriptor entries.
*/
void dispose();
/**
* Return the EPackage to be used for a namespace URI after a platform-resource/plugin URI has already been loaded.
*/
@Nullable EPackage getConflictingGeneratedPackage();
/**
* Return the generated EPackage instance, or null if none loaded.
*/
@Nullable EPackage getEPackage();
/**
* Return the generated EPackage instance without affecting the prevailing status.
* Returns null if an instance cannot be loaded.
*/
@Nullable EPackage getEPackageInstance();
/**
* Return the EPackage resolved by the first loadEPackageByModelURI/loadEPackageByNsURI, or null if none loaded.
*/
@Nullable EPackage getFirstEPackage();
/**
* Return the loaded EPackages, or null if none loaded.
*/
@Nullable EPackage getModel();
/**
* Return the descriptor for the package.
*/
@NonNull IPackageDescriptor getPackageDescriptor();
/**
* Get the status of the resource containing this package.
*/
@NonNull IResourceLoadStatus getResourceLoadStatus();
/**
* Load and return the generated EPackage instance appropriate to the namespace URI.
*/
@Nullable EPackage loadEPackage();
/**
* Define the generated EPackage for this package.
*/
void setEPackage(@NonNull EPackage ePackage);
/**
* Define the loaded EPackage for this package.
*/
void setModel(@NonNull EPackage ePackage);
/**
* Reset the status following notiofication that the model has been unloaded.
*/
void unloadedResource();
}
/**
* An IResourceLoadStrategy determines how each of the possible forms of URI reference to an EPackage should loaded.
*/
public static interface IResourceLoadStrategy
{
/**
* Respond to the explicit addition of a yet to be loaded Ecore model in the user's ResourceSet.
*/
void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource);
/**
* Respond to the explicit addition of a generated EPackage in the user's ResourceSet.
*/
void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage);
/**
* Configure the resourceLoadStatus to udse this strategy and a conflictHandler.
*/
void configure(@NonNull IResourceLoadStatus resourceLoadStatus, @Nullable IConflictHandler conflictHandler);
/**
* Load and return the EPackage in response to an EPackage.Registry access through an EPackageDescriptor.
*/
@Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus);
/**
* Respond to the platform/plugin access to a resource with a resourceLoadStatus containing a
* package already accessed as the Java generated ePackage,
*/
void handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage);
/**
* Respond to the loading of a dynamic Ecore model in the user's ResourceSet.
*/
void loadedDynamicResource(@NonNull IResourceLoadStatus packageLoadStatus, @NonNull Resource resource);
/**
* Respond to the notification that the resource has been unloaded.
*/
void unloadedResource(@NonNull IResourceLoadStatus resourceLoadStatus);
/**
* Respond to the explicit notification of a generated resource.
*/
void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource);
}
protected static abstract class AbstractResourceLoadStrategy implements IResourceLoadStrategy
{
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
throw new UnsupportedOperationException();
}
public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
throw new UnsupportedOperationException();
}
public void configure(@NonNull IResourceLoadStatus resourceLoadStatus, @Nullable IConflictHandler conflictHandler) {
resourceLoadStatus.setConflictHandler(conflictHandler);
resourceLoadStatus.setResourceLoadStrategy(this);
}
public void handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage) {
throw new UnsupportedOperationException();
}
protected @Nullable EPackage loadEPackage(@NonNull IPackageLoadStatus packageLoadStatus, boolean configureURImap) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
resourceLoadStatus.loadGeneratedPackages();
EPackage ePackage = packageLoadStatus.getEPackage();
if (ePackage == null) {
ePackage = packageLoadStatus.getEPackageInstance();
}
if (configureURImap) {
resourceLoadStatus.configureDelegatingResource();//ResourceSetURIResourceMap(ePackage.eResource());
}
return returnEPackage(packageLoadStatus, ePackage);
}
protected @Nullable EPackage loadModel(@NonNull IPackageLoadStatus packageLoadStatus) {
packageLoadStatus.getResourceLoadStatus().loadDynamicResource(packageLoadStatus.getPackageDescriptor().getNsURI());
EPackage ePackage = packageLoadStatus.getModel();
if (ePackage == null) {
ePackage = packageLoadStatus.getEPackageInstance();
}
return returnEPackage(packageLoadStatus, ePackage);
}
public void loadedDynamicResource(@NonNull IResourceLoadStatus packageLoadStatus, @NonNull Resource resource) {}
@Override
public String toString() {
return getClass().getSimpleName();
}
protected @Nullable EPackage returnEPackage(@NonNull IPackageLoadStatus packageLoadStatus, @Nullable EPackage ePackage) {
if (ePackage != null) {
if (PROJECT_MAP_RESOLVE.isActive()) {
URI uri = EcoreUtil.getURI(ePackage);
PROJECT_MAP_RESOLVE.println("EPackage.Registry[" + packageLoadStatus.getPackageDescriptor().getNsURI() + "] => " + uri);
}
}
return ePackage;
}
public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
throw new UnsupportedOperationException();
}
}
/**
* The LoadedStrategy re-uses the already loaded EPackage.
*/
private static final class LoadedStrategy extends AbstractResourceLoadStrategy
{
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
resourceLoadStatus.setResource(resource);
resourceLoadStatus.setResourceLoadStrategy(LoadedStrategy.INSTANCE);
}
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadedStrategy();
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
return packageLoadStatus.getFirstEPackage();
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {}
}
/**
* The LoadedAsGeneratedPackageStrategy re-uses the already loaded EPackage for namespace URI accesses,
* and invokes the conflict handler for platform URI accesses.
*/
private static final class LoadedAsGeneratedPackageStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadedAsGeneratedPackageStrategy();
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
// throw new UnsupportedOperationException();
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
EPackage ePackage = packageLoadStatus.getFirstEPackage();
return returnEPackage(packageLoadStatus, ePackage);
}
@Override
public void handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage) {
resourceLoadStatus.getConflictingDynamicResource(ePackage);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {}
@Override
public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {}
}
/**
* The LoadedFirstAsDynamicResourceStrategy supports the using-model behaviour following a LoadFirstStrategy
* that loaded a model.
*/
private static final class LoadedFirstAsDynamicResourceStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadedFirstAsDynamicResourceStrategy();
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
EPackage ePackage = packageLoadStatus.getConflictingGeneratedPackage();
return returnEPackage(packageLoadStatus, ePackage);
}
@Override
public void loadedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {
packageLoadStatus.unloadedResource();
packageLoadStatus.setResourceLoadStrategy(LoadFirstStrategy.INSTANCE);
}
@Override
public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {}
}
/**
* The LoadBothStrategy permits metamodel schizophrenia and so access to the namespace URI resolves to an installed
* resource while access to the platform plugin or resource URI resolve to a dynamically loaded resource.
*/
public static final class LoadBothStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadBothStrategy();
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
resourceLoadStatus.setResource(resource);
resourceLoadStatus.configureResourceSetURIResourceMap(resource);
resourceLoadStatus.setResourceLoadStrategy(LoadingBothLoadedDynamicResourceStrategy.INSTANCE);
}
@Override
public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
Resource eResource = ePackage.eResource();
if (eResource != null) {
resourceLoadStatus.configureResourceSetURIResourceMap(eResource);
}
resourceLoadStatus.setResourceLoadStrategy(LoadedStrategy.INSTANCE);
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
return loadEPackage(packageLoadStatus, false);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {
packageLoadStatus.unloadedResource();
}
}
/**
* The LoadingBothLoadedDynamicResourceStrategy supports the using-model behaviour following a LoadBothStrategy
* that has loaded a dynamic resource.
*/
public static final class LoadingBothLoadedDynamicResourceStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadingBothLoadedDynamicResourceStrategy();
@Override
public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
Resource eResource = ePackage.eResource();
if (eResource != null) {
resourceLoadStatus.configureResourceSetURIResourceMap(eResource);
}
resourceLoadStatus.setResourceLoadStrategy(LoadedStrategy.INSTANCE);
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
return loadEPackage(packageLoadStatus, false);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {
packageLoadStatus.unloadedResource();
}
}
/**
* The LoadGeneratedPackageStrategy uses the generated EPackage referenced by the namespace URI for all kinds of access,
* and then changes the strategy to the LoadedStrategy for all further accesses.
*/
public static final class LoadGeneratedPackageStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadGeneratedPackageStrategy();
@Override
public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
Resource eResource = ePackage.eResource();
if (eResource != null) {
resourceLoadStatus.configureResourceSetURIResourceMap(eResource);
}
resourceLoadStatus.setResourceLoadStrategy(LoadedStrategy.INSTANCE);
}
@Override
public void configure(@NonNull IResourceLoadStatus resourceLoadStatus, @Nullable IConflictHandler conflictHandler) {
super.configure(resourceLoadStatus, conflictHandler);
resourceLoadStatus.configureDelegatingResource();
}
@Override
public void handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage) {
resourceLoadStatus.getConflictingDynamicResource(ePackage);
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
return loadEPackage(packageLoadStatus, false);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {}
}
/**
* The LoadFirstStrategy uses the EPackage corresponding to the first access as either a namespace URI
* or platform plugin.resource URI.Thereafter accesses to the same URI use the first loaded EPackage.
* Accesses to the other form of URI are arbitrated by the IConflictHandler in the IResourceLoadStatus.
*/
public static final class LoadFirstStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadFirstStrategy();
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
resourceLoadStatus.configureResourceSetURIResourceMap(resource);
resourceLoadStatus.setResourceLoadStrategy(LoadedFirstAsDynamicResourceStrategy.INSTANCE);
resourceLoadStatus.setResource(resource);
}
@Override
public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
Resource eResource = ePackage.eResource();
if (eResource != null) {
resourceLoadStatus.configureResourceSetURIResourceMap(eResource);
}
resourceLoadStatus.setResourceLoadStrategy(LoadedAsGeneratedPackageStrategy.INSTANCE);
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
EPackage loadEPackage = loadEPackage(packageLoadStatus, true);
packageLoadStatus.getResourceLoadStatus().setResourceLoadStrategy(LoadedAsGeneratedPackageStrategy.INSTANCE);
return loadEPackage;
}
@Override
public void handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage) {
resourceLoadStatus.getConflictingDynamicResource(ePackage);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {}
@Override
public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
resourceLoadStatus.configureResourceSetURIResourceMap(resource);
resourceLoadStatus.setResourceLoadStrategy(LoadedAsGeneratedPackageStrategy.INSTANCE);
}
}
/**
* The LoadDynamicResourceStrategy uses the dynamic EPackage referenced by the platform resource/plugin URI for all kinds of access,
* and then changes the strategy to LoadedStrategy for all further accesses.
*/
public static final class LoadDynamicResourceStrategy extends AbstractResourceLoadStrategy
{
public static final @NonNull IResourceLoadStrategy INSTANCE = new LoadDynamicResourceStrategy();
@Override
public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {
resourceLoadStatus.setResource(resource);
resourceLoadStatus.configureResourceSetURIResourceMap(resource);
resourceLoadStatus.configureEPackageRegistry(resource);
resourceLoadStatus.setResourceLoadStrategy(LoadedStrategy.INSTANCE);
}
public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
return loadModel(packageLoadStatus);
}
public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {}
}
/**
* An IResourceDescriptor describes the modeling capabilities of one or more known
* model packages in a genmodel.
*/
public static interface IResourceDescriptor
{
void addedDynamicResource(@NonNull ResourceSet resourceSet, @NonNull Resource resource);
void addedGeneratedPackage(@NonNull ResourceSet resourceSet, @NonNull EPackage ePackage);
void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler);
void configureResourceSetURIResourceMap(@NonNull ResourceSet resourceSet, @NonNull Resource resource);
/**
* Return the project relative Gen Model URI.
*/
@NonNull URI getGenModelURI();
/**
* Return the external filespace form of the model URI containing the package.
* @throws IllegalStateException if there is no Ecore model.
*/
@NonNull URI getLocationURI();
/**
* Return the descriptors for allpackages in this resource.
*/
Iterable<? extends IPackageDescriptor> getPackageDescriptors();
/**
* Return the platform:/resource form of the model URI containing the package
* @throws IllegalStateException if there is no Ecore model.
*/
@NonNull URI getPlatformResourceURI();
/**
* Return the platform:/plugin form of the model URI containing the package
* @throws IllegalStateException if there is no Ecore model.
*/
@NonNull URI getPlatformPluginURI();
/**
* Return the Project Descriptor containing this resource.
*/
@NonNull IProjectDescriptor getProjectDescriptor();
@NonNull URI getProjectRelativeEcorePackageURI(@NonNull URI genModelRelativeEcorePackageURI);
/**
* Return IResourceLoadStatus for this resource in conjunction with resourceSet.
*/
@NonNull IResourceLoadStatus getResourceLoadStatus(@Nullable ResourceSet resourceSet);
/**
* Return true if setEcoreModel has defined the Ecore Model context.
*/
boolean hasEcoreModel();
/**
* Set the Ecore Model context of the resource from a list of URIs of the Ecore Packages relative to the
* genModelURI, and a map of the package namespace URI to package descriptor.
*/
void setEcoreModel(@NonNull List<String> genModelRelativeEcorePackageUris, @NonNull Map<String, IPackageDescriptor> nsURI2packageDescriptor);
/**
* Unload the package registry to force a reload.
*/
void unload(@NonNull ResourceSet resourceSet);
}
/**
* An IPackageDescriptor describes the modeling capabilities of a known
* model package and may be installed under a variety of synonyms in an
* EPackage.Registry to map multiple URIs to a single EPackage.
*/
public static interface IPackageDescriptor
{
/**
* Configure the resourceSet-specific resource status of for this package to use
* a strategy and a conflictHandler.
*/
void configure(@NonNull ResourceSet resourceSet, @NonNull IResourceLoadStrategy strategy, @Nullable IConflictHandler conflictHandler);
/**
* Return the classname defined in the generated_packaged extension point, or null if undefined.
*/
@Nullable String getClassName();
/**
* Return the Package Namespace URI.
*/
@NonNull URI getNsURI();
/**
* Return the IResourceDescriptor containing this package.
*/
@NonNull IResourceDescriptor getResourceDescriptor();
}
/**
* An IProjectDescriptor describes the capabilities of a project.
*/
public static interface IProjectDescriptor
{
/**
* Call back to add a packageDescriptor to the project.
*/
void addPackageDescriptor(@NonNull IPackageDescriptor packageDescriptor);
/**
* Call back to add a resourceDescriptor to the project.
*/
void addResourceDescriptor(@NonNull IResourceDescriptor resourceDescriptor);
/**
* Configure the resourceSet-specific status of for this resource to use
* a strategy and a conflictHandler.
*/
void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler);
/**
* Create an IResourceDescriptor for a projectRelativeGenModelUri comprsising a map of NsURI to className.
*/
@NonNull IResourceDescriptor createResourceDescriptor(@NonNull String projectRelativeGenModelUri, @NonNull Map<URI, String> nsURI2className);
/**
* Return the physical location of this project.
*/
@NonNull URI getLocationURI();
/**
* Return the physical location of a projectRelativeFileName as a URI.
*/
@NonNull URI getLocationURI(@NonNull String projectRelativeFileName);
/**
* Return the physical location of a projectRelativeFileName as a File.
*/
@NonNull File getLocationFile(@NonNull String projectRelativeFileName);
/**
* Return project name.
*/
@NonNull String getName();
/**
* Return the location of this project as a platform:/plugin URI.
*/
@NonNull URI getPlatformPluginURI();
/**
* Return the location of a projectRelativeFileName as a
* platform:/resource URI.
*/
@NonNull URI getPlatformPluginURI(@NonNull String projectRelativeFileName);
/**
* Return the location of this project as a platform:/resource URI.
*/
@NonNull URI getPlatformResourceURI();
/**
* Return the location of a projectRelativeFileName as a
* platform:/resource URI.
*/
@NonNull URI getPlatformResourceURI(@NonNull String projectRelativeFileName);
/**
* Return the package descriptor for the package with a given nsURI or
* null if none known in the project.
*/
@Nullable IPackageDescriptor getPackageDescriptor(@NonNull URI nsURI);
/**
* Return the overall ProjectMap.
*/
@NonNull StandaloneProjectMap getProjectMap();
/**
* Return all packages descriptors in the project.
*/
@Nullable Collection<IResourceDescriptor> getResourceDescriptors();
void initializeGenModelLocationMap(@NonNull Map<URI, IPackageDescriptor> nsURI2package);
void initializePlatformResourceMap();
void initializeURIMap(@NonNull Map<URI, URI> uriMap);
void unload(@NonNull ResourceSet resourceSet);
}
public static abstract class AbstractResourceLoadStatus implements IResourceLoadStatus, Adapter
{
protected final @NonNull IResourceDescriptor resourceDescriptor;
protected @Nullable ResourceSet resourceSet;
private final @NonNull Map<URI, PackageLoadStatus> nsURI2packageLoadStatus = new HashMap<URI, PackageLoadStatus>();
protected final @NonNull EPackage.Registry packageRegistry;
/**
* The optional handler for namespace/platform or platform/namespace metamodel schizophrenia.
*/
protected @Nullable IConflictHandler conflictHandler = MapToFirstConflictHandlerWithLog.INSTANCE;
/**
* The strategy to be used to resolve further URI to EPackage mappings.
*/
protected @NonNull IResourceLoadStrategy resourceLoadStrategy = LoadFirstStrategy.INSTANCE;
/**
* Target of unload watching Adapter.
*/
private @Nullable Notifier target = null;
/**
* The dynamically loaded model (e.g. platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore#/).
*/
protected @Nullable Resource eModel = null;
/**
* Re-entrancy inhibitor for for generation from Pivot2Ecore.
*/
protected boolean generativeLoadInProgress = false;
/**
* Re-entrancy detector for self-referential models such as Ecore.ecore.
*/
protected boolean recursiveLoadInProgress = false;
protected AbstractResourceLoadStatus(@NonNull IResourceDescriptor resourceDescriptor, @Nullable ResourceSet resourceSet) {
this.resourceDescriptor = resourceDescriptor;
this.resourceSet = resourceSet;
this.packageRegistry = StandaloneProjectMap.getPackageRegistry(resourceSet);
for (IPackageDescriptor packageDescriptor : resourceDescriptor.getPackageDescriptors()) {
nsURI2packageLoadStatus.put(packageDescriptor.getNsURI(), new PackageLoadStatus(this, packageDescriptor));
}
}
public void configureEPackageRegistry(@NonNull Resource resource) {
ResourceSet resourceSet2 = resourceSet;
if (resourceSet2 != null) {
for (IPackageLoadStatus packageLoadStatus : nsURI2packageLoadStatus.values()) {
EPackage ePackage = packageLoadStatus.getEPackage();
if (ePackage != null) {
packageLoadStatus.configureEPackageRegistry(resourceSet2);
}
}
}
}
public void configureDelegatingResource() {
ResourceSet resourceSet2 = resourceSet;
if (resourceSet2 != null) {
Collection<PackageLoadStatus> packageLoadStatuses = nsURI2packageLoadStatus.values();
@SuppressWarnings("null")@NonNull URI uri = resourceDescriptor.getGenModelURI().appendFileExtension("ecore");
Resource resource;
if (packageLoadStatuses.size() == 1) {
@SuppressWarnings("null")@NonNull PackageLoadStatus packageLoadStatus = packageLoadStatuses.iterator().next();
resource = new DelegatedSinglePackageResource(uri, packageLoadStatus);
}
else {
resource = new DelegatedMultiplePackageResource(uri, this, packageLoadStatuses);
}
resourceDescriptor.configureResourceSetURIResourceMap(resourceSet2, resource);
}
}
public void configureResourceSetURIResourceMap(@NonNull Resource resource) {
ResourceSet resourceSet2 = resourceSet;
if (resourceSet2 != null) {
resourceDescriptor.configureResourceSetURIResourceMap(resourceSet2, resource);
}
}
public void dispose() {
resourceSet = null;
if (target != null) {
target.eAdapters().remove(this);
target = null;
}
}
public @Nullable EPackage getConflictingDynamicResource(@NonNull EPackage ePackage) {
if (conflictHandler != null) {
return conflictHandler.handleConflictingDynamicResource(this, ePackage);
}
else {
return null;
}
}
public @Nullable EPackage getFirstEPackage() {
throw new UnsupportedOperationException();
}
public @Nullable IPackageLoadStatus getPackageLoadStatus(@NonNull IPackageDescriptor packageDescriptor) {
return nsURI2packageLoadStatus.get(packageDescriptor.getNsURI());
}
public @NonNull IResourceLoadStrategy getResourceLoadStrategy() {
return resourceLoadStrategy;
}
public @NonNull EPackage.Registry getPackageRegistry() {
return packageRegistry;
}
public @NonNull IResourceDescriptor getResourceDescriptor() {
return resourceDescriptor;
}
public @Nullable Resource getResource() {
Resource eModel2 = eModel;
if (eModel2 == null) {
try {
recursiveLoadInProgress = true;
IResourceDescriptor resourceDescriptor2 = getResourceDescriptor();
if (resourceDescriptor2.hasEcoreModel()) {
URI platformResourceURI = resourceDescriptor2.getPlatformResourceURI();
ResourceSet resourceSet = this.resourceSet != null ? this.resourceSet : new ResourceSetImpl();
resourceSet.createResource(platformResourceURI); // Calls back to addedResource()
}
} finally {
recursiveLoadInProgress = false;
}
}
return eModel;
}
public @Nullable ResourceSet getResourceSet() {
return resourceSet;
}
public @Nullable Notifier getTarget() {
return target;
}
public @Nullable EPackage handleConflictingGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus) {
if ((conflictHandler != null) && (eModel != null)) {
return conflictHandler.handleConflictingGeneratedPackage(packageLoadStatus, eModel);
}
else {
EPackage ePackage = packageLoadStatus.getEPackage();
if (ePackage == null) {
ePackage = packageLoadStatus.getEPackageInstance();
}
return ePackage;
}
}
protected void handleLoadException(Resource resource, final @NonNull String location, Exception exception) throws RuntimeException {
class DiagnosticWrappedException extends WrappedException implements Resource.Diagnostic
{
private static final long serialVersionUID = 1L;
public DiagnosticWrappedException(Exception exception) {
super(exception);
}
public String getLocation() {
return location;
}
public int getColumn() {
return 0;
}
public int getLine() {
return 0;
}
}
Exception cause = exception instanceof Resource.IOWrappedException ? (Exception) exception.getCause() : exception;
DiagnosticWrappedException wrappedException = new DiagnosticWrappedException(cause);
if ((resource != null) && resource.getErrors().isEmpty()) {
resource.getErrors().add(exception instanceof Resource.Diagnostic ? (Resource.Diagnostic) exception : wrappedException);
}
throw wrappedException;
}
protected void install() {}
public boolean isAdapterForType(Object type) {
return false;
}
public void loadGeneratedPackages() {
for (IPackageLoadStatus packageLoadStatus : nsURI2packageLoadStatus.values()) {
packageLoadStatus.loadEPackage();
}
}
public synchronized @Nullable Resource loadDynamicResource(@NonNull URI nsURI) {
if (recursiveLoadInProgress) { // Recursive load
logger.error("Attempt to load self-referential '" + nsURI + "' as model replaced by registered EPackage");
return eModel;
}
return getResource();
}
private void loadedDynamicResource(@NonNull ResourceSet resourceSet, @NonNull EPackage ePackage) {
String nsURI = ePackage.getNsURI();
if (nsURI != null) {
StandaloneProjectMap projectMap = resourceDescriptor.getProjectDescriptor().getProjectMap();
@SuppressWarnings("null")@NonNull URI uri = URI.createURI(nsURI);
IPackageDescriptor packageDescriptor = projectMap.getPackageDescriptor(uri);
if (packageDescriptor != null) {
IPackageLoadStatus packageLoadStatus = getPackageLoadStatus(packageDescriptor);
if (packageLoadStatus != null) {
packageLoadStatus.setModel(ePackage);
if (PROJECT_MAP_RESOLVE.isActive()) {
PROJECT_MAP_RESOLVE.println(ePackage.getNsURI() + " => " + ePackage.eResource().getURI() + " : " + DomainUtil.debugSimpleName(ePackage));
}
}
}
}
for (EPackage eSubPackage : ePackage.getESubpackages()) {
if (eSubPackage != null) {
loadedDynamicResource(resourceSet, eSubPackage);
}
}
}
private void loadedDynamicResource(@NonNull Resource newResource) {
ResourceSet resourceSet = newResource.getResourceSet();
if (resourceSet != null) {
for (EObject eObject : newResource.getContents()) {
if (eObject instanceof EPackage) {
EPackage ePackage = (EPackage) eObject;
loadedDynamicResource(resourceSet, ePackage);
}
}
}
resourceLoadStrategy.loadedDynamicResource(this, newResource);
}
public void notifyChanged(Notification notification) {
if (notification.getNotifier() == target) {
int id = notification.getFeatureID(Resource.class);
if (id == Resource.RESOURCE__IS_LOADED) {
int eventType = notification.getEventType();
if (eventType == Notification.SET) {
boolean wasLoaded = notification.getOldBooleanValue();
boolean isLoaded = notification.getNewBooleanValue();
if (isLoaded && !wasLoaded) {
if (target instanceof Resource) {
loadedDynamicResource((Resource) target);
}
}
else if (!isLoaded && wasLoaded) {
resourceLoadStrategy.unloadedResource(this);
}
}
}
}
}
public void setConflictHandler(@Nullable IConflictHandler conflictHandler) {
this.conflictHandler = conflictHandler;
}
/**
* Set true by Pivot2Ecore to inhibit auto-loading of newly added EPackages.
*/
public void setGenerationInProgress(boolean isGenerating) {
assert !recursiveLoadInProgress;
generativeLoadInProgress = isGenerating;
}
public void setResource(@NonNull Resource resource) {
assert eModel == null;
eModel = resource;
if (!resource.isLoaded() && !generativeLoadInProgress) {
try {
InputStream inputStream = resource.getResourceSet().getURIConverter().createInputStream(resource.getURI());
List<Adapter> eAdapters = resource.eAdapters();
if (!eAdapters.contains(this)) {
eAdapters.add(this);
}
resource.load(inputStream, null);
} catch (Exception exception) {
handleLoadException(resource, DomainUtil.nonNullEMF(resource.getURI().toString()), exception);
}
}
}
public void setResourceLoadStrategy(@NonNull IResourceLoadStrategy resourceLoadStrategy) {
this.resourceLoadStrategy = resourceLoadStrategy;
if (PROJECT_MAP_CONFIGURE.isActive()) {
PROJECT_MAP_CONFIGURE.println(this.toString());
}
}
public void setTarget(Notifier newTarget) {
this.target = newTarget;
}
@Override
public @NonNull String toString() {
StringBuilder s = new StringBuilder();
s.append(resourceLoadStrategy + " for " + resourceDescriptor.getGenModelURI());
if (packageRegistry == EPackage.Registry.INSTANCE) {
s.append(" in global ");
}
else {
s.append(" in ");
}
s.append(DomainUtil.debugSimpleName(packageRegistry));
@SuppressWarnings("null")@NonNull String string = s.toString();
return string;
}
public void unloadedResource() {
for (IPackageLoadStatus packageLoadStatus : nsURI2packageLoadStatus.values()) {
packageLoadStatus.unloadedResource();
}
}
}
public static final class SinglePackageResourceLoadStatus extends AbstractResourceLoadStatus
{
public SinglePackageResourceLoadStatus(@NonNull SinglePackageResourceDescriptor packageDescriptor, @Nullable ResourceSet resourceSet) {
super(packageDescriptor, resourceSet);
install();
}
}
/**
* A DelegatedMultiplePackageResource may be installed in a ResourceSet.uriResourceMap so that the
* appropriate generated EPackage is resolved as the fragment of a dynamically loaded
* resource. Conflicts may be diagnosed durng the delegation.
* <p>
* This Resource should never be used for any other purpose.
*/
public static class DelegatedMultiplePackageResource extends ResourceImpl
{
protected final @NonNull IResourceLoadStatus resourceLoadStatus;
protected final @NonNull Iterable<PackageLoadStatus> packageLoadStatuses;
private final @NonNull Map<String, EPackage> fragment2ePackage = new HashMap<String, EPackage>();
public DelegatedMultiplePackageResource(@NonNull URI uri, @NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Iterable<PackageLoadStatus> packageLoadStatuses) {
super(uri);
this.resourceLoadStatus = resourceLoadStatus;
this.packageLoadStatuses = packageLoadStatuses;
for (IPackageLoadStatus packageLoadStatus : packageLoadStatuses) {
EPackage ePackage = packageLoadStatus.loadEPackage();
if (ePackage != null) {
StringBuilder s = new StringBuilder();
computeFragment(s, ePackage);
fragment2ePackage.put(s.toString(), ePackage);
}
}
setLoaded(true); // FIXME Defer till needed
}
@Override
public EObject getEObject(String uriFragment) {
if (uriFragment == null) {
return null;
}
EPackage ePackage = fragment2ePackage.get(uriFragment);
EObject eObject = ePackage;
if (eObject == null) {
for (String uri : fragment2ePackage.keySet()) {
if (uriFragment.startsWith(uri)) {
ePackage = fragment2ePackage.get(uri);
// String uriSuffix = uriFragment.substring(uri.length());
Resource resource = ePackage.eResource();
eObject = resource.getEObject(uriFragment);
if (eObject != null) {
break;
}
}
}
}
if (ePackage != null) {
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
resourceLoadStrategy.handleConflictingDynamicResource(resourceLoadStatus, ePackage);
return eObject;
}
return null;
}
private void computeFragment(@NonNull StringBuilder s, @NonNull EPackage ePackage) {
EPackage eSuperPackage = ePackage.getESuperPackage();
if (eSuperPackage == null) {
s.append("/");
}
else {
computeFragment(s, eSuperPackage);
s.append("/");
s.append(ePackage.getName());
}
}
}
/**
* A DelegatedSinglePackageResource may be installed in a ResourceSet.uriResourceMap so that the
* generated EPackage is resolved as a dynamically loaded resource.
* Conflicts may be diagnosed durng the delegation.
* <p>
* This Resource should never be used for any other purpose.
*/
public static class DelegatedSinglePackageResource extends ResourceImpl
{
private static @NonNull EList<EObject> EMPTY_LIST = new BasicEList.UnmodifiableEList<EObject>(0, new Object[]{});
protected final @NonNull IPackageLoadStatus packageLoadStatus;
private final @Nullable EPackage ePackage;
private final @Nullable Resource eResource;
public DelegatedSinglePackageResource(@NonNull URI uri, @NonNull IPackageLoadStatus packageLoadStatus) {
super(uri);
this.packageLoadStatus = packageLoadStatus;
ePackage = packageLoadStatus.loadEPackage();
eResource = ePackage != null ? ePackage.eResource() : null;
setLoaded(true);
}
@Override
public EList<EObject> getContents() {
return eResource != null ? eResource.getContents() : EMPTY_LIST;
}
@Override
public EObject getEObject(String uriFragment) {
if (uriFragment == null) {
return null;
}
EPackage ePackage2 = ePackage;
if (ePackage2 == null) {
return null;
}
Resource resource = ePackage2.eResource();
EObject eObject = resource.getEObject(uriFragment);
if (eObject != null) {
IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus();
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
resourceLoadStrategy.handleConflictingDynamicResource(resourceLoadStatus, ePackage2);
}
return eObject;
}
public @NonNull Resource getResource() {
return eResource != null ? eResource : this;
}
}
public static class MultiplePackageResourceLoadStatus extends AbstractResourceLoadStatus
{
public MultiplePackageResourceLoadStatus(@NonNull MultiplePackageResourceDescriptor resourceDescriptor, @Nullable ResourceSet resourceSet) {
super(resourceDescriptor, resourceSet);
install();
}
}
public static final class PackageLoadStatus implements IPackageLoadStatus
{
protected final @NonNull AbstractResourceLoadStatus resourceLoadStatus;
protected final @NonNull IPackageDescriptor packageDescriptor;
protected final @NonNull EPackageDescriptor namespaceURIDescriptor;
/**
* The EPackage resulting from the first loadEPackageByModelURI/loadEPackageByNsURI
*/
private @Nullable EPackage firstEPackage;
/**
* The resolved compiled EPackage (e.g. org.eclipse.emf.ecore.EcorePackage.eINSTANCE).
*/
private @Nullable EPackage ePackage = null;
/**
* The resolved EPackage from a dynamically loaded model (e.g. platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore#/).
*/
private @Nullable EPackage eModel = null;
public PackageLoadStatus(@NonNull AbstractResourceLoadStatus resourceLoadStatus, @NonNull IPackageDescriptor packageDescriptor) {
this.resourceLoadStatus = resourceLoadStatus;
this.packageDescriptor = packageDescriptor;
this.namespaceURIDescriptor = new EPackageDescriptor(this, resourceLoadStatus.getPackageRegistry());
}
public void configureEPackageRegistry(@NonNull ResourceSet resourceSet) {
URI nsURI = packageDescriptor.getNsURI();
IPackageLoadStatus packageLoadStatus = resourceLoadStatus.getPackageLoadStatus(packageDescriptor);
if (packageLoadStatus != null) {
EPackage modelEPackage = packageLoadStatus.getModel();
resourceSet.getPackageRegistry().put(nsURI.toString(), modelEPackage);
if (PROJECT_MAP_RESOLVE.isActive()) {
PROJECT_MAP_RESOLVE.println("EPackage.Registry[" + nsURI + "] => " + EcoreUtil.getURI(modelEPackage));
}
}
}
public void dispose() {
namespaceURIDescriptor.uninstall(resourceLoadStatus.getPackageRegistry());
firstEPackage = null;
ePackage = null;
eModel = null;
}
public @Nullable EPackage getConflictingGeneratedPackage() {
return resourceLoadStatus.handleConflictingGeneratedPackage(this);
}
public @Nullable EPackage getEPackage() {
if (ePackage == null) {
ePackage = getEPackageInstance();
}
return ePackage;
}
public @Nullable EPackage getEPackageInstance() {
String className = packageDescriptor.getClassName();
if (className != null) {
try {
Class<?> javaClass = Class.forName(className);
Field field = javaClass.getField("eINSTANCE");
return (EPackage) field.get(null);
} catch (ClassNotFoundException e) {
throw new WrappedException(e);
} catch (IllegalAccessException e) {
throw new WrappedException(e);
} catch (NoSuchFieldException e) {
throw new WrappedException(e);
}
} else {
Object object = EPackage.Registry.INSTANCE.get(packageDescriptor.getNsURI().toString());
if (object instanceof EPackage) {
return (EPackage) object;
} else if (object instanceof EPackage.Descriptor) {
return ((EPackage.Descriptor) object).getEPackage();
}
}
return null;
}
public @Nullable EPackage getFirstEPackage() {
if (firstEPackage != null) {
return firstEPackage;
}
else {
return getEPackageInstance();
}
}
public @Nullable EPackage getModel() {
return eModel;
}
public @NonNull IPackageDescriptor getPackageDescriptor() {
return packageDescriptor;
}
public @NonNull IResourceLoadStrategy getResourceLoadStrategy() {
return resourceLoadStatus.getResourceLoadStrategy();
}
public @NonNull IResourceLoadStatus getResourceLoadStatus() {
return resourceLoadStatus;
}
public @Nullable EPackage loadEPackage() {
if (ePackage == null) {
ePackage = getEPackageInstance();
}
if (firstEPackage == null) {
firstEPackage = ePackage;
}
return ePackage;
}
public void setEPackage(@NonNull EPackage ePackage) {
assert this.ePackage == null;
if (firstEPackage == null) {
firstEPackage = ePackage;
}
this.ePackage = ePackage;
}
public void setModel(@NonNull EPackage ePackage) {
assert this.eModel == null;
if (firstEPackage == null) {
firstEPackage = ePackage;
}
this.eModel = ePackage;
}
@Override
public String toString() {
return packageDescriptor.toString();
}
public void unloadedResource() {
eModel = null;
}
}
/**
* PackageDescriptor supports lazy class loading and initialization of a
* compiled Ecore package. Class loading occurs in the context of the
* ProjectMap, which performs classpath scans, so it is assumed that
* everything is visible. Re-use in a larger context may require a new
* ProjectMap to be created.
*
* If a PackageDescriptor is installed under multiple URIs, the resource
* created by the first load is shared by all subsequent resolutions.
*
* If a PackageDescriptor is set to useModel, the *.ecore file is loaded to
* provide the EPackage, rather than the Java className.
*
* A PackageDescriptor maintains the declared context of a package which may be shared by
* many ResourceSets. In contrast a PackageStatus maintains the actual state of a package
* for a particular EPackage.Registry, each of which may have a distinct ResourceLoadStrategy
* and consequently may not load the same EPackage.
*/
public static abstract class AbstractResourceDescriptor implements IResourceDescriptor
{
/**
* The bundle/project in which this package is defined (e.g. for org.eclipse.emf.ecore).
*/
protected final @NonNull IProjectDescriptor projectDescriptor;
/**
* The project-relative URI of the GenModel for the EPackage (e.g. model/Ecore.genmodel).
*/
protected final @NonNull URI genModelURI;
/**
* The filespace URI of the EPackage (e.g. file:/C:/Eclipse/plugins/org.eclipse.emf.ecore/model/Ecore.ecore).
*/
private @Nullable URI locationURI = null;
/**
* The platform resource URI of the EPackage (e.g. platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore).
*/
private @Nullable URI platformResourceURI = null;
/**
* The platform plugin URI of the EPackage (e.g. platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore).
*/
private @Nullable URI platformPluginURI = null;
/**
* The package descriptors for each of the multiple packages in the genmodel.
*/
protected final @NonNull List<IPackageDescriptor> packageDescriptors = new ArrayList<IPackageDescriptor>();
private boolean hasEcoreModel = false;
/**
* The IResourceLoadStatus for each ResourceSet (null is the global 'ResourceSet' 'containing' all Java'd packages).
*/
private final @NonNull WeakHashMap<ResourceSet, IResourceLoadStatus> resourceSet2resourceLoadStatus = new WeakHashMap<ResourceSet, IResourceLoadStatus>();
protected AbstractResourceDescriptor(@NonNull IProjectDescriptor projectDescriptor, @NonNull URI genModelURI, @NonNull Map<URI, String> nsURI2className) {
this.projectDescriptor = projectDescriptor;
this.genModelURI = genModelURI;
for (@SuppressWarnings("null")@NonNull URI nsURI : nsURI2className.keySet()) {
@SuppressWarnings("null")@NonNull String className = nsURI2className.get(nsURI);
IPackageDescriptor packageDescriptor = projectDescriptor.getPackageDescriptor(nsURI);
if (packageDescriptor == null) {
if (PROJECT_MAP_ADD_GENERATED_PACKAGE.isActive()) {
PROJECT_MAP_ADD_GENERATED_PACKAGE.println(nsURI + " : " + genModelURI + " : " + className);
}
packageDescriptor = new PackageDescriptor(this, nsURI, className);
projectDescriptor.addPackageDescriptor(packageDescriptor);
packageDescriptors.add(packageDescriptor);
}
}
projectDescriptor.addResourceDescriptor(this);
}
public void addedDynamicResource(@NonNull ResourceSet resourceSet, @NonNull Resource resource) {
IResourceLoadStatus resourceLoadStatus = resourceSet2resourceLoadStatus.get(resourceSet);
if (resourceLoadStatus != null) {
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
if (PROJECT_MAP_GET.isActive()) {
PROJECT_MAP_GET.println("Add " + resource.getURI() + " with " + resourceLoadStrategy + " in " + DomainUtil.debugSimpleName(resourceSet.getPackageRegistry()));
}
resourceLoadStrategy.addedDynamicResource(resourceLoadStatus, resource);
}
}
public void addedGeneratedPackage(@NonNull ResourceSet resourceSet, @NonNull EPackage ePackage) {
IResourceLoadStatus resourceLoadStatus = resourceSet2resourceLoadStatus.get(resourceSet);
@SuppressWarnings("null")@NonNull URI uri = URI.createURI(ePackage.getNsURI());
IPackageDescriptor packageDescriptor = getProjectDescriptor().getPackageDescriptor(uri);
if (packageDescriptor != null) {
IPackageLoadStatus packageLoadStatus = resourceLoadStatus.getPackageLoadStatus(packageDescriptor);
if (packageLoadStatus != null) {
resourceLoadStatus.getResourceLoadStrategy().addedGeneratedPackage(packageLoadStatus, ePackage);
}
}
}
public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) {
if (hasEcoreModel) {
IResourceLoadStatus resourceLoadStatus = getResourceLoadStatus(resourceSet);
resourceLoadStrategy.configure(resourceLoadStatus, conflictHandler);
}
}
public void configureResourceSetURIResourceMap(@NonNull ResourceSet resourceSet, @NonNull Resource resource) {
Map<URI, Resource> uriResourceMap;
synchronized (resourceSet) {
uriResourceMap = ((ResourceSetImpl)resourceSet).getURIResourceMap();
if (uriResourceMap == null) {
uriResourceMap = new HashMap<URI, Resource>();
((ResourceSetImpl)resourceSet).setURIResourceMap(uriResourceMap);
}
}
URI platformPluginURI = getPlatformPluginURI();
URI platformResourceURI = getPlatformResourceURI();
synchronized (uriResourceMap) {
uriResourceMap.put(platformPluginURI, resource);
uriResourceMap.put(platformResourceURI, resource);
}
if (PROJECT_MAP_RESOLVE.isActive()) {
URI uri = resource.getURI();
PROJECT_MAP_RESOLVE.println("ResourceSet.uriResourceMap[" + platformPluginURI + "] => " + uri);
PROJECT_MAP_RESOLVE.println("ResourceSet.uriResourceMap[" + platformResourceURI + "] => " + uri);
}
}
protected abstract @NonNull IResourceLoadStatus createResourceLoadStatus(@Nullable ResourceSet resourceSet);
public @NonNull URI getGenModelURI() {
return genModelURI;
}
public @NonNull URI getLocationURI() {
return DomainUtil.nonNullState(locationURI);
}
public @NonNull List<? extends IPackageDescriptor> getPackageDescriptors() {
return packageDescriptors;
}
public @NonNull URI getPlatformPluginURI() {
return DomainUtil.nonNullState(platformPluginURI);
}
public @NonNull URI getPlatformResourceURI() {
return DomainUtil.nonNullState(platformResourceURI);
}
public @NonNull IProjectDescriptor getProjectDescriptor() {
return projectDescriptor;
}
public @NonNull URI getProjectRelativeEcorePackageURI(@NonNull URI genModelRelativeEcorePackageURI) {
URI projectLocationURI = projectDescriptor.getLocationURI();
URI absoluteGenModelURI = genModelURI.resolve(projectLocationURI);
URI absolutePackageURI = genModelRelativeEcorePackageURI.resolve(absoluteGenModelURI);
@SuppressWarnings("null")@NonNull URI projectRelativeEcorePackageURI = absolutePackageURI.deresolve(projectLocationURI, true, true, true);
return projectRelativeEcorePackageURI;
}
public @NonNull IResourceLoadStatus getResourceLoadStatus(@Nullable ResourceSet resourceSet) {
assert hasEcoreModel;
IResourceLoadStatus resourceLoadStatus = resourceSet2resourceLoadStatus.get(resourceSet);
if (resourceLoadStatus == null) {
synchronized (resourceSet2resourceLoadStatus) {
resourceLoadStatus = resourceSet2resourceLoadStatus.get(resourceSet);
if (resourceLoadStatus == null) {
resourceLoadStatus = createResourceLoadStatus(resourceSet);
resourceSet2resourceLoadStatus.put(resourceSet, resourceLoadStatus);
}
}
}
return resourceLoadStatus;
}
public boolean hasEcoreModel() {
return hasEcoreModel;
}
public void setEcoreModel(@NonNull List<String> genModelRelativeEcorePackageUris, @NonNull Map<String, IPackageDescriptor> nsURI2packageDescriptor) {
int size = genModelRelativeEcorePackageUris.size();
if (size > 0) {
@SuppressWarnings("null")@NonNull String firstGenModelRelativeEcorePackageUri = genModelRelativeEcorePackageUris.get(0);
URI firstGenModelRelativeEcorePackageURI = URI.createURI(firstGenModelRelativeEcorePackageUri);
@SuppressWarnings("null")@NonNull URI genModelRelativeEcoreModelURI = firstGenModelRelativeEcorePackageURI.trimFragment();
URI projectLocationURI = projectDescriptor.getLocationURI();
URI absoluteGenModelURI = genModelURI.resolve(projectLocationURI);
URI absolutePackageURI = genModelRelativeEcoreModelURI.resolve(absoluteGenModelURI);
URI relativePackageURI = absolutePackageURI.deresolve(projectLocationURI, true, true, true);
@SuppressWarnings("null")@NonNull URI relativeEcoreModelURI = relativePackageURI.trimFragment();
URI resourceURI = projectDescriptor.getPlatformResourceURI();
URI pluginURI = projectDescriptor.getPlatformPluginURI();
platformResourceURI = relativeEcoreModelURI.resolve(resourceURI);
platformPluginURI = relativeEcoreModelURI.resolve(pluginURI);
locationURI = relativeEcoreModelURI.resolve(projectLocationURI);
projectDescriptor.getProjectMap().addResourceDescriptor(this);
}
hasEcoreModel = true;
}
public void unload(@NonNull ResourceSet resourceSet) {
if (hasEcoreModel()) {
synchronized (resourceSet2resourceLoadStatus) {
IResourceLoadStatus resourceLoadStatus = resourceSet2resourceLoadStatus.remove(resourceSet);
if (resourceLoadStatus != null) {
resourceLoadStatus.dispose();
}
}
}
}
}
/**
* PackageDescriptor supports lazy class loading and initialization of a
* compiled Ecore package. Class loading occurs in the context of the
* ProjectMap, which performs classpath scans, so it is assumed that
* everything is visible. Re-use in a larger context may require a new
* ProjectMap to be created.
*
* If a PackageDescriptor is installed under multiple URIs, the resource
* created by the first load is shared by all subsequent resolutions.
*
* If a PackageDescriptor is set to useModel, the *.ecore file is loaded to
* provide the EPackage, rather than the Java className.
*
* A PackageDescriptor maintains the declared context of a package which may be shared by
* many ResourceSets. In contrast a PackageStatus maintains the actual state of a package
* for a particular EPackage.Registry, each of which may have a distinct ResourceLoadStrategy
* and consequently may not load the same EPackage.
*/
public static final class PackageDescriptor implements IPackageDescriptor
{
/**
* The MultiplePackageResourceDescriptor if this PackageDescriptor is part of a multi-package genmodel.
*/
protected final @NonNull IResourceDescriptor resourceDescriptor;
/**
* The namespace URI of the EPackage (e.g. http://www.eclipse.org/emf/2002/Ecore).
*/
protected final @NonNull URI namespaceURI;
/**
* The Java class name of the compiled EPackage (e.g. org.eclipse.emf.ecore.EcorePackage).
*/
protected final @Nullable String className;
public PackageDescriptor(@NonNull IResourceDescriptor resourceDescriptor, @NonNull URI nsURI, @Nullable String className) {
this.resourceDescriptor = resourceDescriptor;
this.namespaceURI = nsURI;
this.className = className;
}
public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) {
resourceDescriptor.configure(resourceSet, resourceLoadStrategy, conflictHandler);
}
public @Nullable String getClassName() {
return className;
}
public @Nullable EFactory getEFactory() {
return null;
}
public @NonNull URI getNsURI() {
return namespaceURI;
}
@SuppressWarnings("null")
public @NonNull List<? extends IPackageDescriptor> getPackageDescriptors() {
return Collections.singletonList(this);
}
public @NonNull IResourceDescriptor getResourceDescriptor() {
return resourceDescriptor;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(namespaceURI);
s.append(" => ");
s.append(className);
// s.append(", ");
// s.append(genModelURI);
return s.toString();
}
}
/**
* PackageDescriptor supports lazy class loading and initialization of a
* compiled Ecore package. Class loading occurs in the context of the
* ProjectMap, which performs classpath scans, so it is assumed that
* everything is visible. Re-use in a larger context may require a new
* ProjectMap to be created.
*
* If a PackageDescriptor is installed under multiple URIs, the resource
* created by the first load is shared by all subsequent resolutions.
*
* If a PackageDescriptor is set to useModel, the *.ecore file is loaded to
* provide the EPackage, rather than the Java className.
*
* A PackageDescriptor maintains the declared context of a package which may be shared by
* many ResourceSets. In contrast a PackageStatus maintains the actual state of a package
* for a particular EPackage.Registry, each of which may have a distinct ResourceLoadStrategy
* and consequently may not load the same EPackage.
*/
public static final class SinglePackageResourceDescriptor extends AbstractResourceDescriptor
{
public SinglePackageResourceDescriptor(@NonNull IProjectDescriptor projectDescriptor, @NonNull URI genModelURI, @NonNull Map<URI, String> nsURI2className) {
super(projectDescriptor, genModelURI, nsURI2className);
}
@Override
protected @NonNull IResourceLoadStatus createResourceLoadStatus(@Nullable ResourceSet resourceSet) {
return new SinglePackageResourceLoadStatus(this, resourceSet);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
// s.append(namespaceURI);
// s.append(" => ");
// s.append(className);
// s.append(", ");
s.append(genModelURI);
// if (ecorePackageURI != null) {
// s.append(", ");
// s.append(ecorePackageURI);
// }
return s.toString();
}
}
/**
* PackageDescriptor supports lazy class loading and initialization of a
* compiled Ecore package. Class loading occurs in the context of the
* ProjectMap, which performs classpath scans, so it is assumed that
* everything is visible. Re-use in a larger context may require a new
* ProjectMap to be created.
*
* If a PackageDescriptor is installed under multiple URIs, the resource
* created by the first load is shared by all subsequent resolutions.
*
* If a PackageDescriptor is set to useModel, the *.ecore file is loaded to
* provide the EPackage, rather than the Java className.
*
* A PackageDescriptor maintains the declared context of a package which may be shared by
* many ResourceSets. In contrast a PackageStatus maintains the actual state of a package
* for a particular EPackage.Registry, each of which may have a distinct ResourceLoadStrategy
* and consequently may not load the same EPackage.
*/
public static final class MultiplePackageResourceDescriptor extends AbstractResourceDescriptor
{
public MultiplePackageResourceDescriptor(@NonNull ProjectDescriptor projectDescriptor, @NonNull URI genModelURI, @NonNull Map<URI, String> nsURI2className) {
super(projectDescriptor, genModelURI, nsURI2className);
}
@Override
protected @NonNull IResourceLoadStatus createResourceLoadStatus(@Nullable ResourceSet resourceSet) {
return new MultiplePackageResourceLoadStatus(this, resourceSet);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("{");
boolean isFirst = true;
for (IPackageDescriptor packageDescriptor : packageDescriptors) {
if (!isFirst) {
s.append(",");
}
s.append(packageDescriptor.getNsURI());
isFirst = false;
}
s.append("} => ");
// s.append(className);
// s.append(", ");
s.append(genModelURI);
// if (ecorePackageURI != null) {
// s.append(", ");
// s.append(ecorePackageURI);
// }
return s.toString();
}
}
/**
* PluginReader provides the SAX callbacks to support reading the
* org.eclipse.emf.ecore.generated_package extension point in a plugin.xml
* file and activating the GenModelReader to process the
* ecorePackage locations.
*/
protected static class PluginReader extends DefaultHandler
{
public static final @NonNull String pluginTag = "plugin";
public static final @NonNull String extensionTag = "extension";
public static final @NonNull String pointTag = "point";
public static final @NonNull String packageTag = "package";
public static final @NonNull String extensionPointAttribute = "org.eclipse.emf.ecore.generated_package";
public static final @NonNull String uriAttribute = "uri";
public static final @NonNull String classAttribute = "class";
public static final @NonNull String genModelAttribute = "genModel";
protected final JarFile jarFile;
protected final IProjectDescriptor projectDescriptor;
private int pluginCount = 0;
private int extensionCount = 0;
private boolean inPoint = false;
private int packageCount = 0;
private @NonNull Map<String, GenModelReader> genModelReaders = new HashMap<String, GenModelReader>();
private @Nullable Map<String, Map<URI, String>> genModelURI2nsURI2className = null;
private PluginReader(@Nullable IProjectDescriptor projectDescriptor) {
this.jarFile = null;
this.projectDescriptor = projectDescriptor;
}
public PluginReader(@NonNull JarFile jarFile, @NonNull IProjectDescriptor projectDescriptor) {
this.jarFile = jarFile;
this.projectDescriptor = projectDescriptor;
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
Map<String, Map<URI, String>> genModelURI2nsURI2className2 = genModelURI2nsURI2className;
if (genModelURI2nsURI2className2 != null) {
for (@SuppressWarnings("null")@NonNull String genModel : genModelURI2nsURI2className2.keySet()) {
@SuppressWarnings("null")@NonNull Map<URI, String> nsURI2className = genModelURI2nsURI2className2.get(genModel);
IResourceDescriptor resourceDescriptor = projectDescriptor.createResourceDescriptor(genModel, nsURI2className);
GenModelReader genModelReader = new GenModelReader(resourceDescriptor);
genModelReaders.put(genModel, genModelReader);
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (pluginCount == 1) {
if (pluginTag.equals(qName)) {
pluginCount--;
}
if (extensionCount == 1) {
if (extensionTag.equals(qName)) {
extensionCount--;
}
if (packageCount == 1) {
if (packageTag.equals(qName)) {
packageCount--;
}
}
}
}
}
public void scanContents(SAXParser saxParser) throws SAXParseException {
for (String genModel : genModelReaders.keySet()) {
GenModelReader genModelReader = genModelReaders.get(genModel);
URI locationURI = projectDescriptor.getLocationURI();
URI genModelURI = URI.createURI(genModel).resolve(locationURI);
InputStream inputStream = null;
try {
if (jarFile != null) {
ZipEntry entry = jarFile.getEntry(genModel);
if (entry != null) {
inputStream = jarFile.getInputStream(entry);
}
} else {
inputStream = new FileInputStream(genModelURI.isFile() ? genModelURI.toFileString() : genModelURI.toString());
}
if (inputStream != null) {
saxParser.parse(inputStream, genModelReader);
}
} catch (Exception e) {
throw new SAXParseException("Failed to parse " + locationURI, null, e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
}
}
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (pluginCount == 0) {
if (pluginTag.equals(qName)) {
pluginCount++;
}
} else if (pluginCount == 1) {
if ((extensionCount == 0) && extensionTag.equals(qName)) {
extensionCount++;
inPoint = extensionPointAttribute.equals(attributes.getValue(pointTag));
} else if ((extensionCount == 1) && inPoint) {
if ((packageCount == 0) && packageTag.equals(qName)) {
packageCount++;
String className = attributes.getValue(classAttribute);
@SuppressWarnings("null")@NonNull URI nsURI = URI.createURI(attributes.getValue(uriAttribute));
String genModel = attributes.getValue(genModelAttribute);
if ((genModel != null) && (className != null)) {
Map<String, Map<URI, String>> genModelURI2nsURI2className2 = genModelURI2nsURI2className;
if (genModelURI2nsURI2className2 == null) {
genModelURI2nsURI2className = genModelURI2nsURI2className2 = new HashMap<String, Map<URI, String>>();
}
Map<URI, String> nsURI2className = genModelURI2nsURI2className2.get(genModel);
if (nsURI2className == null) {
nsURI2className = new HashMap<URI, String>();
genModelURI2nsURI2className2.put(genModel, nsURI2className);
}
nsURI2className.put(nsURI, className);
}
}
}
}
}
}
/**
* GenModelReader provides the SAX callbacks to support reading
* the genPackages element in a genmodel file.
*/
protected static class GenModelReader extends DefaultHandler
{
public static final @NonNull String genmodelTag = "genmodel:GenModel";
public static final @NonNull String genPackagesTag = "genPackages";
public static final @NonNull String nestedGenPackagesTag = "nestedGenPackages";
public static final @NonNull String ecorePackageTag = "ecorePackage";
public static final @NonNull String ecorePackageAttribute = "ecorePackage";
public static final @NonNull String hrefAttribute = "href";
protected final @NonNull IResourceDescriptor resourceDescriptor;
protected final @NonNull IProjectDescriptor projectDescriptor;
protected final @NonNull Map<String, IPackageDescriptor> nsURI2packageDescriptor = new HashMap<String, IPackageDescriptor>();
protected final @NonNull URI genModelURI;
protected final @NonNull List<String> ecorePackages = new ArrayList<String>();
private @NonNull Stack<String> elements = new Stack<String>();
/**
* A simple public static method that may be used to force class
* initialization.
*/
public static void initStatics() {}
public GenModelReader(@NonNull IResourceDescriptor resourceDescriptor) {
this.resourceDescriptor = resourceDescriptor;
this.projectDescriptor = resourceDescriptor.getProjectDescriptor();
this.genModelURI = resourceDescriptor.getGenModelURI();
for (@SuppressWarnings("null")@NonNull IPackageDescriptor packageDescriptor : resourceDescriptor.getPackageDescriptors()) {
add(packageDescriptor);
}
}
private void add(@NonNull IPackageDescriptor packageDescriptor) {
nsURI2packageDescriptor.put(packageDescriptor.getNsURI().toString(), packageDescriptor);
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
try {
resourceDescriptor.setEcoreModel(ecorePackages, nsURI2packageDescriptor);
}
catch (Exception e) {
logger.warn("Failed lo read " + genModelURI, e);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
elements.pop();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
int size = elements.size();
if (genPackagesTag.equals(qName)) {
if ((size == 1) && genmodelTag.equals(elements.elementAt(0))) {
String ecorePackage = attributes.getValue(ecorePackageAttribute);
if (ecorePackage != null) {
ecorePackages.add(ecorePackage);
}
}
}
else if (nestedGenPackagesTag.equals(qName)) {
if ((size > 1) && genPackagesTag.equals(elements.elementAt(1)) && genmodelTag.equals(elements.elementAt(0))) {
String ecorePackage = attributes.getValue(ecorePackageAttribute);
if (ecorePackage != null) {
ecorePackages.add(ecorePackage);
}
}
}
else if (ecorePackageTag.equals(qName)) {
if ((size >= 2) && genPackagesTag.equals(elements.elementAt(1)) && genmodelTag.equals(elements.elementAt(0))) {
String topElement = elements.elementAt(size-1);
if (genPackagesTag.equals(topElement) || nestedGenPackagesTag.equals(topElement)) {
String ecorePackage = attributes.getValue(hrefAttribute);
if (ecorePackage != null) {
ecorePackages.add(ecorePackage);
}
}
}
}
elements.push(qName);
}
}
public static class ProjectDescriptor implements IProjectDescriptor
{
/**
* The overall ProjectMap
*/
protected final @NonNull StandaloneProjectMap projectMap;
/**
* The project/bundle/plugin name; e.g. "org.eclipse.emf.ecore"
*/
protected final @NonNull String name;
/**
* The resolveable location.
*/
protected final @NonNull URI locationURI;
/**
* Map from local Model URI to lazy EPackageDescriptor. e.g. from "model/Ecore.ecore".
*/
private @Nullable Map<URI, IPackageDescriptor> nsURI2packageDescriptor = null;
private @Nullable Map<URI, IResourceDescriptor> genModelURI2packageDescriptor = null;
public ProjectDescriptor(@NonNull StandaloneProjectMap projectMap, @NonNull String name, @NonNull URI locationURI) {
this.projectMap = projectMap;
this.name = name;
this.locationURI = locationURI;
}
public void addPackageDescriptor(@NonNull IPackageDescriptor packageDescriptor) {
Map<URI, IPackageDescriptor> nsURI2packageDescriptor2 = nsURI2packageDescriptor;
if (nsURI2packageDescriptor2 == null) {
nsURI2packageDescriptor = nsURI2packageDescriptor2 = new HashMap<URI, IPackageDescriptor>();
}
nsURI2packageDescriptor2.put(packageDescriptor.getNsURI(), packageDescriptor);
}
public void addResourceDescriptor(@NonNull IResourceDescriptor resourceDescriptor) {
Map<URI, IResourceDescriptor> genModelURI2packageDescriptor2 = genModelURI2packageDescriptor;
if (genModelURI2packageDescriptor2 == null) {
genModelURI2packageDescriptor = genModelURI2packageDescriptor2 = new HashMap<URI, IResourceDescriptor>();
}
genModelURI2packageDescriptor2.put(resourceDescriptor.getGenModelURI(), resourceDescriptor);
}
public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) {
if (genModelURI2packageDescriptor != null) {
for (IResourceDescriptor resourceDescriptor : genModelURI2packageDescriptor.values()) {
resourceDescriptor.configure(resourceSet, resourceLoadStrategy, conflictHandler);
}
}
}
public @NonNull IResourceDescriptor createResourceDescriptor(@NonNull String genModel, @NonNull Map<URI, String> nsURI2className) {
URI absoluteGenModelURI = URI.createURI(genModel).resolve(locationURI);
@SuppressWarnings("null")@NonNull URI projectGenModelURI = absoluteGenModelURI.deresolve(locationURI, true, true, true);
if (nsURI2className.size() <= 1) {
return new SinglePackageResourceDescriptor(this, projectGenModelURI, nsURI2className);
}
else {
return new MultiplePackageResourceDescriptor(this, projectGenModelURI, nsURI2className);
}
}
public @NonNull URI getLocationURI() {
return locationURI;
}
@SuppressWarnings("null")
public @NonNull URI getLocationURI(@NonNull String projectRelativeFileName) {
return URI.createURI(projectRelativeFileName).resolve(locationURI);
}
public @NonNull File getLocationFile(@NonNull String projectRelativeFileName) {
return new File(getLocationURI(projectRelativeFileName).toFileString());
}
public @NonNull String getName() {
return name;
}
public @Nullable IPackageDescriptor getPackageDescriptor(@NonNull URI nsURI) {
return nsURI2packageDescriptor != null ? nsURI2packageDescriptor.get(nsURI) : null;
}
public @Nullable Collection<IResourceDescriptor> getResourceDescriptors() {
return genModelURI2packageDescriptor != null ? genModelURI2packageDescriptor.values() : null;
}
@SuppressWarnings("null")
public @NonNull URI getPlatformPluginURI() {
return URI.createPlatformPluginURI("/" + name + "/", true);
}
@SuppressWarnings("null")
public @NonNull URI getPlatformPluginURI(@NonNull String projectRelativeFileName) {
return URI.createURI(projectRelativeFileName).resolve(getPlatformPluginURI());
}
@SuppressWarnings("null")
public @NonNull URI getPlatformResourceURI() {
return URI.createPlatformResourceURI("/" + name + "/", true);
}
@SuppressWarnings("null")
public @NonNull URI getPlatformResourceURI(@NonNull String projectRelativeFileName) {
return URI.createURI(projectRelativeFileName).resolve(getPlatformResourceURI());
}
public @NonNull StandaloneProjectMap getProjectMap() {
return projectMap;
}
public void initializeGenModelLocationMap(@NonNull Map<URI, IPackageDescriptor> nsURI2package) {
Collection<IResourceDescriptor> resourceDescriptors = getResourceDescriptors();
if (resourceDescriptors != null) {
Map<String, URI> ePackageNsURIToGenModelLocationMap = EMF_2_9.EcorePlugin.getEPackageNsURIToGenModelLocationMap(false);
for (IResourceDescriptor resourceDescriptor : resourceDescriptors) {
URI genModelURI = resourceDescriptor.getGenModelURI();
URI resolvedURI = genModelURI.resolve(locationURI);
for (IPackageDescriptor packageDescriptor : resourceDescriptor.getPackageDescriptors()) {
URI nsURI = packageDescriptor.getNsURI();
String nsURIstring = nsURI.toString();
ePackageNsURIToGenModelLocationMap.put(nsURIstring, resolvedURI);
nsURI2package.put(nsURI, packageDescriptor);
if (PROJECT_MAP_ADD_GEN_MODEL.isActive()) {
PROJECT_MAP_ADD_GEN_MODEL.println(nsURI + " => " + resolvedURI);
}
}
}
}
}
public void initializePlatformResourceMap() {
Map<String, URI> platformResourceMap = EcorePlugin.getPlatformResourceMap();
platformResourceMap.put(name, locationURI);
}
public void initializeURIMap(@NonNull Map<URI, URI> uriMap) {
URI resourceURI = getPlatformResourceURI();
URI pluginURI = getPlatformPluginURI();
uriMap.put(resourceURI, locationURI);
uriMap.put(pluginURI, locationURI);
if (PROJECT_MAP_ADD_URI_MAP.isActive()) {
PROJECT_MAP_ADD_URI_MAP.println(resourceURI + " => " + locationURI);
PROJECT_MAP_ADD_URI_MAP.println(pluginURI + " => " + locationURI);
}
}
public void unload(@NonNull ResourceSet resourceSet) {
Collection<IResourceDescriptor> resourceDescriptors = getResourceDescriptors();
if (resourceDescriptors != null) {
for (IResourceDescriptor resourceDescriptor : resourceDescriptors) {
assert resourceDescriptor != null;
resourceDescriptor.unload(resourceSet);
}
Map<URI, IPackageDescriptor> nsURI2packageDescriptor2 = nsURI2packageDescriptor;
EPackage.Registry packageRegistry = resourceSet.getPackageRegistry();
if ((nsURI2packageDescriptor2 != null) && (packageRegistry != null)) {
for (URI nsURI : nsURI2packageDescriptor2.keySet()) {
packageRegistry.remove(nsURI.toString());
}
}
}
}
@Override
public String toString() {
return name + " => " + locationURI.toString();
}
}
/**
* An IConflictHandler confligures the hanling of conflicting access between generated packages and
* dynamically loaded resources.
*/
public static interface IConflictHandler
{
/**
* Return the EPackage to be used for a namespace URI reference after the model EPackage has already been used.
*/
@Nullable EPackage handleConflictingGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull Resource resource);
/**
* Return the EPackage to be used for a model URI reference after the namespace EPackage has already been used.
*/
@Nullable EPackage handleConflictingDynamicResource(@NonNull IResourceLoadStatus packageLoadStatus, @NonNull EPackage ePackage);
}
/**
* MapToFirstConflictHandler resolves conflicts by returning the first loaded EPackage.
*/
public static class MapToFirstConflictHandler implements IConflictHandler
{
public static final @NonNull IConflictHandler INSTANCE = new MapToFirstConflictHandler();
public @Nullable EPackage handleConflictingGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull Resource resource) {
return packageLoadStatus.getFirstEPackage();
}
public @Nullable EPackage handleConflictingDynamicResource(@NonNull IResourceLoadStatus packageLoadStatus, @NonNull EPackage ePackage) {
return ePackage;
}
}
/**
* MapToFirstConflictHandler resolves conflicts by returning the first loaded EPackage.
*/
public static class MapToFirstConflictHandlerWithLog implements IConflictHandler
{
public static final @NonNull IConflictHandler INSTANCE = new MapToFirstConflictHandlerWithLog();
public @Nullable EPackage handleConflictingGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull Resource resource) {
EPackage firstEPackage = packageLoadStatus.getFirstEPackage();
IPackageDescriptor packageDescriptor = packageLoadStatus.getPackageDescriptor();
logger.error("Conflicting access to '" + packageDescriptor.getNsURI() + "' already accessed as '" + resource.getURI() + "'");
packageLoadStatus.getResourceLoadStatus().setConflictHandler(MapToFirstConflictHandler.INSTANCE);
return firstEPackage;
}
public @Nullable EPackage handleConflictingDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull EPackage ePackage) {
IResourceDescriptor resourceDescriptor = resourceLoadStatus.getResourceDescriptor();
logger.error("Conflicting access to '" + resourceDescriptor.getPlatformResourceURI() +
"' or '" + resourceDescriptor.getPlatformPluginURI() +
"' already accessed as '" + ePackage.getNsURI() + "'");
resourceLoadStatus.setConflictHandler(MapToFirstConflictHandler.INSTANCE);
return ePackage;
}
}
/**
* Return any StandaloneProjectMap already installed as an adapter on a
* <tt>resourceSet</tt>. Returns null if there is no such adapter.
*/
public static @Nullable StandaloneProjectMap findAdapter(@NonNull ResourceSet resourceSet) {
return (StandaloneProjectMap) EcoreUtil.getAdapter(
resourceSet.eAdapters(), StandaloneProjectMap.class);
}
/**
* Return the StandaloneProjectMap already installed as an adapter on a
* <tt>resourceSet</tt> if one exists, else creates, installs, initializes
* and returns a new StandaloneProjectMap.
*/
public static @NonNull StandaloneProjectMap getAdapter(@NonNull ResourceSet resourceSet) {
StandaloneProjectMap adapter = findAdapter(resourceSet);
if (adapter == null) {
adapter = new StandaloneProjectMap();
// resourceSet.eAdapters().add(adapter);
adapter.initializeResourceSet(resourceSet);
}
return adapter;
}
/**
* Eliminate all facilities used by the ProjectMap for the resourceSet.
*/
public static void dispose(@NonNull ResourceSet resourceSet) {
StandaloneProjectMap projectMap = findAdapter(resourceSet);
if (projectMap != null) {
projectMap.unload(resourceSet);
projectMap.unsetTarget(resourceSet);
}
}
/**
* Return the EPackage.Registry for a resourceSet or the Global
* {@link org.eclipse.emf.ecore.EPackage.Registry#INSTANCE} if resourceSet is null.
*/
public static @NonNull EPackage.Registry getPackageRegistry(@Nullable ResourceSet resourceSet) {
if (resourceSet == null) {
@SuppressWarnings("null")
@NonNull
EPackage.Registry globalRegistry = EPackage.Registry.INSTANCE;
return globalRegistry;
} else {
@SuppressWarnings("null")
@NonNull
EPackage.Registry packageRegistry = resourceSet.getPackageRegistry();
return packageRegistry;
}
}
/**
* Return the Resource.Factory.Registry for a resourceSet or the Global
* {@link org.eclipse.emf.ecore.resource.Resource.Factory.Registry#INSTANCE} if resourceSet is null.
*/
public static Resource.Factory.Registry getResourceFactoryRegistry(@Nullable ResourceSet resourceSet) {
return resourceSet != null
? resourceSet.getResourceFactoryRegistry()
: Resource.Factory.Registry.INSTANCE;
}
/**
* Return the URIConverter for a resourceSet or the Global
* {@link URIConverter#INSTANCE} if resourceSet is null.
*/
@SuppressWarnings("null")
public static @NonNull URIConverter getURIConverter(@Nullable ResourceSet resourceSet) {
return resourceSet != null ? resourceSet.getURIConverter() : URIConverter.INSTANCE;
}
/**
* Return the URI Map for a resourceSet or the Global
* {@link URIConverter#URI_MAP} if resourceSet is null.
*/
@SuppressWarnings("null")
public static @NonNull Map<URI, URI> getURIMap(@Nullable ResourceSet resourceSet) {
return resourceSet != null ? resourceSet.getURIConverter().getURIMap() : URIConverter.URI_MAP;
}
/**
* A simple public static method that may be used to force class
* initialization.
*/
public static void initStatics() {
GenModelReader.initStatics();
new PluginReader(null);
}
/**
* Activate any ResourceSetImpl.uriResourceMap so that repeated lookups use
* a hash rather than linear search.
*/
public static void initializeURIResourceMap(@Nullable ResourceSet resourceSet) {
if (resourceSet instanceof ResourceSetImpl) {
ResourceSetImpl resourceSetImpl = (ResourceSetImpl) resourceSet;
Map<URI, Resource> uriResourceMap = resourceSetImpl.getURIResourceMap();
if (uriResourceMap == null) {
resourceSetImpl.setURIResourceMap(new HashMap<URI, Resource>());
}
}
}
/**
* Leak debugging aid. Set non-null to diagnose MetaModelManager construction and finalization.
*/
public static WeakHashMap<StandaloneProjectMap,Object> liveStandaloneProjectMaps = null;
public StandaloneProjectMap() {
super();
if (liveStandaloneProjectMaps != null) {
liveStandaloneProjectMaps.put(this, null);
System.out.println(Thread.currentThread().getName() + " Create " + getClass().getSimpleName()
+ "@" + Integer.toHexString(System.identityHashCode(this)));
}
}
/**
* Exceptions encountered during processing as a map from File to Exception.
*/
private @Nullable Map<String, Exception> exceptionMap = null;
/**
* The map of bundle/project name to project descriptor.
*/
private Map<String, IProjectDescriptor> project2descriptor = null;
protected boolean initializedPlatformResourceMap = false;
/**
* The map of package nsURI to package descriptor.
*/
protected @Nullable Map<URI, IPackageDescriptor> nsURI2package = null;
/**
* The map of document URI to resource descriptor.
*/
protected @Nullable Map<URI, IResourceDescriptor> uri2resource = null;
/**
* Call-back to add a resourceDescriptor.
*/
public void addResourceDescriptor(@NonNull IResourceDescriptor resourceDescriptor) {
Map<URI, IResourceDescriptor> uri2resource2 = uri2resource;
if (uri2resource2 == null) {
uri2resource = uri2resource2 = new HashMap<URI, IResourceDescriptor>();
}
uri2resource2.put(resourceDescriptor.getPlatformPluginURI(), resourceDescriptor);
uri2resource2.put(resourceDescriptor.getPlatformResourceURI(), resourceDescriptor);
}
/**
* Configure the PackageRegistry associated with ResourceSet to use a resourceLoadStrategy and conflictHandler when
* resolving namespace ansd platform URIs.
*/
public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) {
Map<String, IProjectDescriptor> projectDescriptors = getProjectDescriptors();
if (projectDescriptors != null) {
for (IProjectDescriptor projectDescriptor : projectDescriptors.values()) {
projectDescriptor.configure(resourceSet, resourceLoadStrategy, conflictHandler);
}
}
}
protected @NonNull IProjectDescriptor createProjectDescriptor(@NonNull String projectName, @NonNull URI locationURI) {
return new ProjectDescriptor(this, projectName, locationURI);
}
@Override
protected void finalize() throws Throwable {
if (liveStandaloneProjectMaps != null) {
System.out.println("Finalize " + getClass().getSimpleName()
+ "@" + Integer.toString(System.identityHashCode(this)));
List<StandaloneProjectMap> keySet = new ArrayList<StandaloneProjectMap>(liveStandaloneProjectMaps.keySet());
if (!keySet.isEmpty()) {
StringBuilder s = new StringBuilder();
s.append(" live");
for (StandaloneProjectMap projectMap : keySet) {
s.append(" @" + Integer.toHexString(System.identityHashCode(projectMap)));
}
System.out.println(s);
}
}
}
/**
* Return the IPackageDescriptor for a given nsURI.
*/
public @Nullable IPackageDescriptor getPackageDescriptor(@NonNull URI nsURI) {
return nsURI2package != null ? nsURI2package.get(nsURI) : null;
}
/**
* Return the IProjectDescriptor for a given project or bundle name.
*/
public @Nullable IProjectDescriptor getProjectDescriptor(@NonNull String projectName) {
Map<String, IProjectDescriptor> projectDescriptors = getProjectDescriptors();
if (projectDescriptors == null) {
return null;
}
return projectDescriptors.get(projectName);
}
protected @NonNull IProjectDescriptor getProjectDescriptorInternal(@NonNull URI platformURI) {
@SuppressWarnings("null")@NonNull String projectName = platformURI.segment(1);
getProjectDescriptors();
IProjectDescriptor projectDescriptor = project2descriptor.get(projectName);
if (projectDescriptor == null) {
@SuppressWarnings("null")@NonNull URI locationURI = platformURI.trimSegments(platformURI.segmentCount() - 2).appendSegment("");
projectDescriptor = createProjectDescriptor(projectName, locationURI);
project2descriptor.put(projectName, projectDescriptor);
}
return projectDescriptor;
}
/**
* Return the mapping of problem files to exceptions, or null if not yet
* computed or if no exceptions thrown.
*/
public @Nullable Map<String, Exception> getExceptionMap() {
return exceptionMap;
}
/**
* Return the resolveable URI for a given project or bundle name.
*/
public @Nullable URI getLocation(@NonNull String projectName) {
Map<String, IProjectDescriptor> projectDescriptors = getProjectDescriptors();
if (projectDescriptors == null) {
return null;
}
IProjectDescriptor projectDescriptor = projectDescriptors.get(projectName);
if (projectDescriptor == null) {
return null;
}
return projectDescriptor.getLocationURI();
}
/**
* Return the mapping of project name or bundle name, as defined in a
* manifest file to the location of that project as determined by scanning
* the classpath.
* <p>
* e.g. entries such as <br>
* org.antlr.runtime =>
* archive:file:/C:/Tools/Eclipse/3.7.1/plugins/org.antlr
* .runtime_3.2.0.v201101311130.jar!/ <br>
* org.eclipse.ocl.examples.common =>
* file:/C:/GIT/org.eclipse.ocl/examples/org.eclipse.ocl.examples.common/
* <p>
* Any problems arising while creating the project map are gathered into the
* exception map accessible using {@link #getExceptionMap()}. An overall
* problem may be attributed to the null file.
*/
protected synchronized @Nullable Map<String, IProjectDescriptor> getProjectDescriptors() {
Map<String, IProjectDescriptor> project2descriptor2 = project2descriptor;
if (project2descriptor2 == null) {
project2descriptor = project2descriptor2 = new HashMap<String, IProjectDescriptor>();
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = factory.newSAXParser();
if (saxParser != null) {
scanClassPath(project2descriptor2, saxParser);
}
} catch (Exception e) {
logException("Failed to create SAXParser", e);
return null;
}
}
return project2descriptor2;
}
/**
* Initialize the
* {@link EcorePlugin#getEPackageNsURIToGenModelLocationMap()} so that in a
* standalone environment the locations of all genmodels are available.
* <p>
* Initialization is only necessary once and for a standalone environment.
* If <tt>force</tt> is true a re-initialization or plugin initialization
* may be forced.
*/
public synchronized void initializeGenModelLocationMap(boolean force) {
if (force || (nsURI2package == null)) {
Map<URI, IPackageDescriptor> nsURI2package2 = new HashMap<URI, IPackageDescriptor> ();
nsURI2package = nsURI2package2;
Map<String, IProjectDescriptor> projectDescriptors = getProjectDescriptors();
if (projectDescriptors != null) {
for (IProjectDescriptor projectDescriptor : projectDescriptors.values()) {
projectDescriptor.initializeGenModelLocationMap(nsURI2package2);
}
}
}
}
/**
* Install lazy EPackageDescriptors in the EPackage.Registry for all
* registered packages and their platform:/plugin and platform:/resource
* synonyms, which are determined by examining the genPackages.ecorePackage
* attribute in all genModels.
*/
public synchronized void initializePackageRegistry(@Nullable ResourceSet resourceSet) {
getProjectDescriptors();
for (IProjectDescriptor projectDescriptor : project2descriptor.values()) {
Collection<IResourceDescriptor> resourceDescriptors = projectDescriptor.getResourceDescriptors();
if (resourceDescriptors != null) {
for (IResourceDescriptor resourceDescriptor : resourceDescriptors) {
assert resourceDescriptor != null;
if (resourceDescriptor.hasEcoreModel()) {
IResourceLoadStatus resourceLoadStatus = resourceDescriptor.getResourceLoadStatus(resourceSet);
resourceLoadStatus.setConflictHandler(MapToFirstConflictHandlerWithLog.INSTANCE);
}
}
}
}
}
/**
* Initialize the {@link EcorePlugin#getPlatformResourceMap()} so that in a standalone
* environment and in conjunction with {@link #initializeURIMap(ResourceSet)} URIs such as
* <tt>platform:/resource/<i>project</i></tt> and
* <tt>platform:/plugin/<i>project</i></tt> are useable.
* <p>
* Initialization is only necessary once and for a standalone environment.
* If <tt>force</tt> is true a re-initialization or plugin initialization
* may be forced.
*/
public synchronized void initializePlatformResourceMap(boolean force) {
if (force || !initializedPlatformResourceMap) {
initializedPlatformResourceMap = true;
getProjectDescriptors();
for (IProjectDescriptor projectDescriptor : project2descriptor.values()) {
projectDescriptor.initializePlatformResourceMap();
}
}
}
/**
* Ensure that both the {@link EcorePlugin#getPlatformResourceMap()} and
* {@link ResourceSet#getURIConverter()} are initialized so that
* <tt>platform:/resource/<i>project</i></tt> and
* <tt>platform:/plugin/<i>project</i></tt> are useable..
*
* A null ResourceSet may be used to provoke initialization of the global
* EPackage.Registry.INSTANCE and URIConverter.URI_MAP.
*/
public void initializeResourceSet(@Nullable ResourceSet resourceSet) {
initializeURIResourceMap(resourceSet);
initializePlatformResourceMap(false);
initializeURIMap(resourceSet);
initializeGenModelLocationMap(false);
initializePackageRegistry(resourceSet);
if (resourceSet != null) {
resourceSet.eAdapters().add(this);
}
}
/**
* Initialize the uriMap of a uriConverter so that each of
* <tt>platform:/resource/<i>project</i></tt> and
* <tt>platform:/plugin/<i>project</i></tt> resolve the workspace project
* resource else the plugin bundle for use in either standalone or plugin
* environment.
* <p>
* Note that in a plugin environment, a single <tt>platform:/resource/</tt>
* to <tt>platform:/plugin/</tt> mapping is sufficient since
* <tt>platform:/plugin/</tt> is directly resolveable by the Eclipse
* Platform.
*/
public synchronized void initializeURIMap(@Nullable ResourceSet resourceSet) {
getProjectDescriptors();
Map<URI, URI> uriMap = getURIMap(resourceSet);
for (String project : project2descriptor.keySet()) {
IProjectDescriptor projectDescriptor = project2descriptor.get(project);
projectDescriptor.initializeURIMap(uriMap);
}
}
@Override
public boolean isAdapterForType(Object type) {
return (type instanceof Class<?>)
&& ((Class<?>) type).isAssignableFrom(StandaloneProjectMap.class);
}
protected void logException(@NonNull String message, @NonNull Exception e) {
Set<String> alreadyLogged2 = alreadyLogged;
if (alreadyLogged2 == null) {
alreadyLogged = alreadyLogged2 = new HashSet<String>();
}
if (alreadyLogged2.add(message)) {
logger.info(message, e);
}
Map<String, Exception> exceptionMap2 = exceptionMap;
if (exceptionMap2 == null) {
exceptionMap = exceptionMap2 = new HashMap<String, Exception>();
}
exceptionMap2.put(message, e);
}
/**
* Internal call-back to observe Resource addition to a ResourceSet..
*/
@Override
public void notifyChanged(Notification notification)
{
Object notifier = notification.getNotifier();
if (notifier instanceof ResourceSet) {
int eventType = notification.getEventType();
int featureID = notification.getFeatureID(ResourceSet.class);
if (featureID == ResourceSet.RESOURCE_SET__RESOURCES) {
if (eventType == Notification.ADD) {
Object newValue = notification.getNewValue();
if (newValue instanceof Resource) {
notifyAddedDynamicResource((ResourceSet)notifier, (Resource)newValue);
}
}
else if (eventType == Notification.ADD_MANY) {
Object newValues = notification.getNewValue();
if (newValues instanceof Iterable<?>) {
for (Object newValue : (Iterable<?>)newValues){
if (newValue instanceof Resource) {
notifyAddedDynamicResource((ResourceSet)notifier, (Resource)newValue);
}
}
}
}
}
}
}
/**
* When a new Resource is added to a watched ResourceSet notify the resourceDescriptor that there is a new
* (ResourceSet, Resource) pair so that it install both platform:/plugin and platform:/resource
* entries in the ResourceSet's uriResourceMap and install a listener to detect when the Resource is loaded.
*/
protected void notifyAddedDynamicResource(@NonNull ResourceSet resourceSet, @NonNull Resource resource) {
// resource.eAdapters().add(this);
if (resourceSet instanceof ResourceSetImpl) {
Map<URI, IResourceDescriptor> uri2resource2 = uri2resource;
if (uri2resource2 != null) {
URI uri = resource.getURI();
IResourceDescriptor resourceDescriptor = uri2resource2.get(uri);
if (resourceDescriptor != null) {
resourceDescriptor.addedDynamicResource(resourceSet, resource);
}
}
}
}
protected @Nullable IProjectDescriptor registerBundle(@NonNull File file, @NonNull SAXParser saxParser) {
JarFile jarFile = null;
try {
jarFile = new JarFile(file);
Manifest manifest = jarFile.getManifest();
if (manifest == null) {
return null;
}
String project = manifest.getMainAttributes().getValue("Bundle-SymbolicName");
if (project != null) {
final int indexOf = project.indexOf(';');
if (indexOf > 0) {
project = project.substring(0, indexOf);
}
IProjectDescriptor projectDescriptor = project2descriptor.get(project);
if (projectDescriptor != null)
return projectDescriptor;
String path = "archive:" + file.toURI() + "!/";
@SuppressWarnings("null")@NonNull URI locationURI = URI.createURI(path);
assert project != null;
projectDescriptor = createProjectDescriptor(project, locationURI);
project2descriptor.put(project, projectDescriptor);
ZipEntry entry = jarFile.getEntry("plugin.xml");
if (entry != null) {
InputStream inputStream = jarFile.getInputStream(entry);
try {
PluginReader pluginReader = new PluginReader(jarFile, projectDescriptor);
saxParser.parse(inputStream, pluginReader);
pluginReader.scanContents(saxParser);
} finally {
inputStream.close();
}
}
return projectDescriptor;
}
} catch (ZipException e) {
logException("Could not open Jar file '" + file.getAbsolutePath() + "'", e);
} catch (Exception e) {
logException("Failed to read '" + file.getAbsolutePath() + "'", e);
} finally{
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {}
}
}
return null;
}
protected @Nullable IProjectDescriptor registerProject(@NonNull File file) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);
String project = document.getDocumentElement().getElementsByTagName("name").item(0).getTextContent();
if (project != null) {
@SuppressWarnings("null")@NonNull URI locationURI = URI.createFileURI(file.getParentFile().getCanonicalPath() + File.separator);
IProjectDescriptor projectDescriptor = createProjectDescriptor(project, locationURI);
project2descriptor.put(project, projectDescriptor);
return projectDescriptor;
}
} catch (Exception e) {
logException("Couldn't read '" + file + "'", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
return null;
}
protected void scanClassPath(@NonNull Map<String, IProjectDescriptor> projectDescriptors, @NonNull SAXParser saxParser) {
String property = System.getProperty("java.class.path");
String separator = System.getProperty("path.separator");
if (property != null) {
String[] entries = property.split(separator);
for (String entry : entries) {
File fileEntry = new File(entry);
try {
File f = fileEntry.getCanonicalFile();
if (f.getPath().endsWith(".jar")) {
registerBundle(f, saxParser);
} else if (!scanFolder(f, saxParser, new HashSet<String>(), 0)) {
// eclipse bin folder?
File parentFile = f.getParentFile();
File dotProject = new File(parentFile, ".project");
if (dotProject.exists()) {
IProjectDescriptor projectDescriptor = registerProject(dotProject);
if (projectDescriptor != null) {
File plugIn = new File(parentFile, "plugin.xml");
if (plugIn.exists()) {
PluginReader pluginReader = new PluginReader(projectDescriptor);
saxParser.parse(plugIn, pluginReader);
pluginReader.scanContents(saxParser);
}
}
}
}
} catch (Exception e) {
logException("Failed to read '" + fileEntry + "'", e);
}
}
}
}
protected boolean scanFolder(@NonNull File f, @NonNull SAXParser saxParser, @NonNull Set<String> alreadyVisited, int depth) {
try {
if (!alreadyVisited.add(f.getCanonicalPath()))
return true;
} catch (Exception e) {
logException("Failed to scan '" + f + "'", e);
return true;
}
File[] files = f.listFiles();
boolean containsProject = false;
File dotProject = null;
if (files != null) {
for (File file : files) {
if (file.exists() && file.isDirectory() && (depth < 2) && !file.getName().startsWith(".")) {
containsProject |= scanFolder(file, saxParser, alreadyVisited, depth + 1);
} else if (".project".equals(file.getName())) {
dotProject = file;
} else if (file.getName().endsWith(".jar")) {
registerBundle(file, saxParser);
}
}
}
if (!containsProject && dotProject != null)
registerProject(dotProject);
return containsProject || dotProject != null;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
if (project2descriptor != null) {
List<String> projectNames = new ArrayList<String>(project2descriptor.keySet());
Collections.sort(projectNames);
for (String projectName : projectNames) {
if (s.length() > 0) {
s.append("\n");
}
s.append(projectName);
s.append(" => ");
s.append(project2descriptor.get(projectName).getLocationURI());
}
}
return s.toString();
}
public void unload(@NonNull ResourceSet resourceSet) {
resourceSet.eAdapters().remove(this);
if (project2descriptor != null) {
for (IProjectDescriptor projectDescriptor : project2descriptor.values()) {
projectDescriptor.unload(resourceSet);
}
}
}
/**
* Use a registered resource for use in conjunction with resourceSet. This must be invoked explicitly to ensure
* that conflicting generated/model access is resolved consistently.
*/
public void useGeneratedResource(@NonNull Resource resource, @NonNull ResourceSet resourceSet) {
URI uri = resource.getURI();
if (uri != null) {
IPackageDescriptor packageDescriptor = getPackageDescriptor(uri);
if (packageDescriptor != null) {
IResourceDescriptor resourceDescriptor = packageDescriptor.getResourceDescriptor();
IResourceLoadStatus resourceLoadStatus = resourceDescriptor.getResourceLoadStatus(resourceSet);
IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy();
if (PROJECT_MAP_GET.isActive()) {
PROJECT_MAP_GET.println("Use " + uri + " with " + resourceLoadStrategy + " in " + DomainUtil.debugSimpleName(resourceLoadStatus.getPackageRegistry()));
}
resourceLoadStrategy.useGeneratedResource(resourceLoadStatus, resource);
}
}
}
}