/******************************************************************************* * Copyright (c) 2011, 2015 Willink Transformations 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.pivot.internal.resource; 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.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.pivot.internal.compatibility.EMF_2_9; import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal; import org.eclipse.ocl.pivot.resource.ProjectManager; import org.eclipse.ocl.pivot.resource.ProjectManager.IProjectDescriptor.IProjectDescriptorExtension; import org.eclipse.ocl.pivot.util.PivotPlugin; import org.eclipse.ocl.pivot.utilities.ClassUtil; import org.eclipse.ocl.pivot.utilities.NameUtil; import org.eclipse.ocl.pivot.utilities.TracingOption; 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.domain/</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 implements ProjectManager { private static final String PLUGIN_ID = PivotPlugin.PLUGIN_ID; 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, EPackage.@NonNull Registry packageRegistry) { this.packageLoadStatus = packageLoadStatus; packageRegistry.put(getURI().toString(), this); if (PROJECT_MAP_INSTALL.isActive()) { PROJECT_MAP_INSTALL.println(toString()); } } public @Nullable EPackage basicGetEPackage() { IResourceLoadStatus resourceLoadStatus = packageLoadStatus.getResourceLoadStatus(); IResourceLoadStrategy resourceLoadStrategy = resourceLoadStatus.getResourceLoadStrategy(); if (PROJECT_MAP_GET.isActive()) { PROJECT_MAP_GET.println("BasicGet " + getURI() + " with " + resourceLoadStrategy + " in " + NameUtil.debugSimpleName(resourceLoadStatus.getPackageRegistry())); } return resourceLoadStrategy.basicGetEPackage(packageLoadStatus); } @Override public EFactory getEFactory() { EPackage ePackage = basicGetEPackage(); if (ePackage != null) { return ePackage.getEFactoryInstance(); } else { return null; } } @Override 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 " + NameUtil.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(EPackage.@NonNull Registry packageRegistry) { if (PROJECT_MAP_INSTALL.isActive()) { PROJECT_MAP_INSTALL.println("" + toString()); } packageRegistry.put(getURI().toString(), null); } } protected static abstract class AbstractResourceLoadStrategy implements IResourceLoadStrategy { @Override public void addedDynamicResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) { throw new UnsupportedOperationException(); } @Override public void addedGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull EPackage ePackage) { throw new UnsupportedOperationException(); } @Override public @Nullable EPackage basicGetEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return null; } @Override public void configure(@NonNull IResourceLoadStatus resourceLoadStatus, @Nullable IConflictHandler conflictHandler) { resourceLoadStatus.setConflictHandler(conflictHandler); resourceLoadStatus.setResourceLoadStrategy(this); } @Override 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); } @Override 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; } @Override 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(); @Override public @Nullable EPackage basicGetEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return getEPackage(packageLoadStatus); } @Override public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return packageLoadStatus.getFirstEPackage(); } @Override 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(); } @Override public @Nullable EPackage basicGetEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return getEPackage(packageLoadStatus); } @Override 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); } @Override 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) {} @Override public @Nullable EPackage basicGetEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return getEPackage(packageLoadStatus); } @Override 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) {} @Override 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); } @Override public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return loadEPackage(packageLoadStatus, false); } @Override public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) { packageLoadStatus.unloadedResource(); } @Override public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {} } /** * 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); } @Override public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return loadEPackage(packageLoadStatus, false); } @Override 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); } @Override public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return loadEPackage(packageLoadStatus, false); } @Override public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {} @Override public void useGeneratedResource(@NonNull IResourceLoadStatus resourceLoadStatus, @NonNull Resource resource) {} } /** * 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); } @Override 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); } @Override 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); } @Override public @Nullable EPackage getEPackage(@NonNull IPackageLoadStatus packageLoadStatus) { return loadModel(packageLoadStatus); } @Override public void unloadedResource(@NonNull IResourceLoadStatus packageLoadStatus) {} } 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 EPackage.@NonNull 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 AS2Ecore. */ 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)); } } @Override 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); } } } } @Override public void configureDelegatingResource() { ResourceSet resourceSet2 = resourceSet; if (resourceSet2 != null) { Collection<PackageLoadStatus> packageLoadStatuses = nsURI2packageLoadStatus.values(); @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); } } @Override public void configureResourceSetURIResourceMap(@NonNull Resource resource) { ResourceSet resourceSet2 = resourceSet; if (resourceSet2 != null) { resourceDescriptor.configureResourceSetURIResourceMap(resourceSet2, resource); } } @Override public void dispose() { resourceSet = null; if (target != null) { target.eAdapters().remove(this); target = null; } } @Override public @Nullable EPackage getConflictingDynamicResource(@NonNull EPackage ePackage) { if (conflictHandler != null) { return conflictHandler.handleConflictingDynamicResource(this, ePackage); } else { return null; } } @Override public @Nullable EPackage getFirstEPackage() { throw new UnsupportedOperationException(); } @Override public @Nullable IPackageLoadStatus getPackageLoadStatus(@NonNull IPackageDescriptor packageDescriptor) { return nsURI2packageLoadStatus.get(packageDescriptor.getNsURI()); } @Override public @NonNull IResourceLoadStrategy getResourceLoadStrategy() { return resourceLoadStrategy; } @Override public EPackage.@NonNull Registry getPackageRegistry() { return packageRegistry; } @Override 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; } @Override public @Nullable ResourceSet getResourceSet() { return resourceSet; } @Override 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); } @Override public String getLocation() { return location; } @Override public int getColumn() { return 0; } @Override 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() {} @Override public boolean isAdapterForType(Object type) { return false; } @Override public void loadGeneratedPackages() { for (IPackageLoadStatus packageLoadStatus : nsURI2packageLoadStatus.values()) { packageLoadStatus.loadEPackage(); } } @Override 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) { ProjectManager projectMap = resourceDescriptor.getProjectDescriptor().getProjectManager(); @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() + " : " + NameUtil.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); } @Override 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); } } } } } @Override public void setConflictHandler(@Nullable IConflictHandler conflictHandler) { this.conflictHandler = conflictHandler; } /** * Set true by AS2Ecore to inhibit auto-loading of newly added EPackages. */ @Override public void setGenerationInProgress(boolean isGenerating) { assert !recursiveLoadInProgress; generativeLoadInProgress = isGenerating; } @Override 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); // FIXME BUG 465326 this can load what was only created } catch (Exception exception) { handleLoadException(resource, ClassUtil.nonNullEMF(resource.getURI().toString()), exception); } } } @Override public void setResourceLoadStrategy(@NonNull IResourceLoadStrategy resourceLoadStrategy) { this.resourceLoadStrategy = resourceLoadStrategy; if (PROJECT_MAP_CONFIGURE.isActive()) { PROJECT_MAP_CONFIGURE.println(this.toString()); } } @Override 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(NameUtil.debugSimpleName(packageRegistry)); return s.toString(); } @Override 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, @NonNull EPackage> fragment2ePackage = new HashMap<String, @NonNull 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); assert ePackage != null; // 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 during 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<@NonNull 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()); } @Override 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)); } } } @Override public void dispose() { namespaceURIDescriptor.uninstall(resourceLoadStatus.getPackageRegistry()); firstEPackage = null; ePackage = null; eModel = null; } @Override public @Nullable EPackage getConflictingGeneratedPackage() { return resourceLoadStatus.handleConflictingGeneratedPackage(this); } @Override public @Nullable EPackage getEPackage() { if (ePackage == null) { ePackage = getEPackageInstance(); } return ePackage; } @Override public @Nullable EPackage getEPackageInstance() { String className = packageDescriptor.getClassName(); if (className != null) { try { Class<?> javaClass = Thread.currentThread().getContextClassLoader().loadClass(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; } @Override public @Nullable EPackage getFirstEPackage() { if (firstEPackage != null) { return firstEPackage; } else { return getEPackageInstance(); } } @Override public @Nullable EPackage getModel() { return eModel; } @Override public @NonNull IPackageDescriptor getPackageDescriptor() { return packageDescriptor; } public @NonNull IResourceLoadStrategy getResourceLoadStrategy() { return resourceLoadStatus.getResourceLoadStrategy(); } @Override public @NonNull IResourceLoadStatus getResourceLoadStatus() { return resourceLoadStatus; } @Override public @Nullable EPackage loadEPackage() { if (ePackage == null) { ePackage = getEPackageInstance(); } if (firstEPackage == null) { firstEPackage = ePackage; } return ePackage; } @Override public void setEPackage(@NonNull EPackage ePackage) { assert this.ePackage == null; if (firstEPackage == null) { firstEPackage = ePackage; } this.ePackage = ePackage; } @Override 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(); } @Override 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<@NonNull URI, @NonNull String> nsURI2className) { this.projectDescriptor = projectDescriptor; this.genModelURI = genModelURI; for (@NonNull URI nsURI : nsURI2className.keySet()) { 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); } @Override 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 " + NameUtil.debugSimpleName(resourceSet.getPackageRegistry())); } resourceLoadStrategy.addedDynamicResource(resourceLoadStatus, resource); } } @Override public void addedGeneratedPackage(@NonNull ResourceSet resourceSet, @NonNull EPackage ePackage) { IResourceLoadStatus resourceLoadStatus = resourceSet2resourceLoadStatus.get(resourceSet); @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); } } } @Override public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) { if (hasEcoreModel) { IResourceLoadStatus resourceLoadStatus = getResourceLoadStatus(resourceSet); resourceLoadStrategy.configure(resourceLoadStatus, conflictHandler); } } @Override 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); @Override public @NonNull URI getGenModelURI() { return genModelURI; } @Override public @NonNull URI getLocationURI() { return ClassUtil.nonNullState(locationURI); } @Override public @NonNull List<? extends IPackageDescriptor> getPackageDescriptors() { return packageDescriptors; } @Override public @NonNull URI getPlatformPluginURI() { return ClassUtil.nonNullState(platformPluginURI); } @Override public @NonNull URI getPlatformResourceURI() { return ClassUtil.nonNullState(platformResourceURI); } @Override public @NonNull IProjectDescriptor getProjectDescriptor() { return projectDescriptor; } @Override public @NonNull URI getProjectRelativeEcorePackageURI(@NonNull URI genModelRelativeEcorePackageURI) { URI projectLocationURI = projectDescriptor.getLocationURI(); URI absoluteGenModelURI = genModelURI.resolve(projectLocationURI); URI absolutePackageURI = genModelRelativeEcorePackageURI.resolve(absoluteGenModelURI); @NonNull URI projectRelativeEcorePackageURI = absolutePackageURI.deresolve(projectLocationURI, true, true, true); return projectRelativeEcorePackageURI; } @Override 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; } @Override public boolean hasEcoreModel() { return hasEcoreModel; } @Override 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); @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); @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.getProjectManager().addResourceDescriptor(this); } hasEcoreModel = true; } @Override 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; } @Override public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) { resourceDescriptor.configure(resourceSet, resourceLoadStrategy, conflictHandler); } @Override public @Nullable String getClassName() { return className; } public @Nullable EFactory getEFactory() { return null; } @Override public @NonNull URI getNsURI() { return namespaceURI; } public @NonNull List<? extends IPackageDescriptor> getPackageDescriptors() { return Collections.singletonList(this); } @Override 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<@NonNull URI, @NonNull 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<@NonNull URI, @NonNull 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, @NonNull 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, @NonNull Map<URI, String>> genModelURI2nsURI2className2 = genModelURI2nsURI2className; if (genModelURI2nsURI2className2 != null) { for (@SuppressWarnings("null")@NonNull String genModel : genModelURI2nsURI2className2.keySet()) { Map<URI, String> nsURI2className = genModelURI2nsURI2className2.get(genModel); assert nsURI2className != null; 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); @NonNull URI nsURI = URI.createURI(attributes.getValue(uriAttribute)); String genModel = attributes.getValue(genModelAttribute); if ((genModel != null) && (className != null)) { Map<String, @NonNull Map<URI, String>> genModelURI2nsURI2className2 = genModelURI2nsURI2className; if (genModelURI2nsURI2className2 == null) { genModelURI2nsURI2className = genModelURI2nsURI2className2 = new HashMap<String, @NonNull 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 IProjectDescriptorExtension { /** * 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<@NonNull URI, @NonNull IPackageDescriptor> nsURI2packageDescriptor = null; private @Nullable Map<@NonNull URI, @NonNull IResourceDescriptor> genModelURI2packageDescriptor = null; public ProjectDescriptor(@NonNull StandaloneProjectMap projectMap, @NonNull String name, @NonNull URI locationURI) { this.projectMap = projectMap; this.name = name; this.locationURI = locationURI; } @Override public void addPackageDescriptor(@NonNull IPackageDescriptor packageDescriptor) { Map<@NonNull URI, @NonNull IPackageDescriptor> nsURI2packageDescriptor2 = nsURI2packageDescriptor; if (nsURI2packageDescriptor2 == null) { nsURI2packageDescriptor = nsURI2packageDescriptor2 = new HashMap<@NonNull URI, @NonNull IPackageDescriptor>(); } nsURI2packageDescriptor2.put(packageDescriptor.getNsURI(), packageDescriptor); } @Override public void addResourceDescriptor(@NonNull IResourceDescriptor resourceDescriptor) { Map<@NonNull URI, @NonNull IResourceDescriptor> genModelURI2packageDescriptor2 = genModelURI2packageDescriptor; if (genModelURI2packageDescriptor2 == null) { genModelURI2packageDescriptor = genModelURI2packageDescriptor2 = new HashMap<@NonNull URI, @NonNull IResourceDescriptor>(); } genModelURI2packageDescriptor2.put(resourceDescriptor.getGenModelURI(), resourceDescriptor); } @Override public void configure(@Nullable ResourceSet resourceSet, @NonNull IResourceLoadStrategy resourceLoadStrategy, @Nullable IConflictHandler conflictHandler) { if (genModelURI2packageDescriptor != null) { for (IResourceDescriptor resourceDescriptor : genModelURI2packageDescriptor.values()) { try { resourceDescriptor.configure(resourceSet, resourceLoadStrategy, conflictHandler); } catch (Exception e) { logger.error(e); } } } } @Override public @NonNull IResourceDescriptor createResourceDescriptor(@NonNull String genModel, @NonNull Map<@NonNull URI, @NonNull String> nsURI2className) { URI absoluteGenModelURI = URI.createURI(genModel).resolve(locationURI); @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); } } @Override public @NonNull URI getLocationURI() { return locationURI; } @Override public @NonNull URI getLocationURI(@NonNull String projectRelativeFileName) { return URI.createURI(projectRelativeFileName).resolve(locationURI); } @Override public @NonNull File getLocationFile(@NonNull String projectRelativeFileName) { return new File(getLocationURI(projectRelativeFileName).toFileString()); } @Override public @NonNull String getName() { return name; } @Override public @Nullable IPackageDescriptor getPackageDescriptor(@NonNull URI nsURI) { return nsURI2packageDescriptor != null ? nsURI2packageDescriptor.get(nsURI) : null; } @Override public @Nullable Collection<IResourceDescriptor> getResourceDescriptors() { return genModelURI2packageDescriptor != null ? genModelURI2packageDescriptor.values() : null; } @Override public @NonNull URI getPlatformPluginURI() { return URI.createPlatformPluginURI("/" + name + "/", true); } @Override public @NonNull URI getPlatformPluginURI(@NonNull String projectRelativeFileName) { return URI.createURI(projectRelativeFileName).resolve(getPlatformPluginURI()); } @Override public @NonNull URI getPlatformResourceURI() { return URI.createPlatformResourceURI("/" + name + "/", true); } @Override public @NonNull URI getPlatformResourceURI(@NonNull String projectRelativeFileName) { return URI.createURI(projectRelativeFileName).resolve(getPlatformResourceURI()); } /** * @since 1.1 */ @Override public @Nullable Iterable<@NonNull IPackageDescriptor> getPackageDescriptors() { return nsURI2packageDescriptor != null ? nsURI2packageDescriptor.values() : null; } @Override public @NonNull StandaloneProjectMap getProjectManager() { return projectMap; } @Override 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); } } } } } @Override public void initializePlatformResourceMap() { Map<String, URI> platformResourceMap = EcorePlugin.getPlatformResourceMap(); platformResourceMap.put(name, locationURI); } @Override 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); } } @Override 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(); } } /** * MapToFirstConflictHandler resolves conflicts by returning the first loaded EPackage. */ public static class MapToFirstConflictHandler implements IConflictHandler { public static final @NonNull IConflictHandler INSTANCE = new MapToFirstConflictHandler(); @Override public @Nullable EPackage handleConflictingGeneratedPackage(@NonNull IPackageLoadStatus packageLoadStatus, @NonNull Resource resource) { return packageLoadStatus.getFirstEPackage(); } @Override 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(); @Override 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; } @Override 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(false); // resourceSet.eAdapters().add(adapter); adapter.initializeResourceSet(resourceSet); } return adapter; } /** * Return the EPackage.Registry for a resourceSet or the Global * {@link org.eclipse.emf.ecore.EPackage.Registry#INSTANCE} if resourceSet is null. */ public static EPackage.@NonNull Registry getPackageRegistry(@Nullable ResourceSet resourceSet) { if (resourceSet == null) { @SuppressWarnings("null") EPackage.@NonNull Registry globalRegistry = EPackage.Registry.INSTANCE; return globalRegistry; } else { @SuppressWarnings("null") EPackage.@NonNull 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; /** * Whether this is the Global Project Manager */ protected final boolean isGlobal; /** * 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, @NonNull 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; public StandaloneProjectMap(boolean isGlobal) { super(); this.isGlobal = isGlobal; if (liveStandaloneProjectMaps != null) { liveStandaloneProjectMaps.put(this, null); PivotUtilInternal.debugPrintln("Create " + getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))); } } /** * Call-back to add a resourceDescriptor. */ @Override 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. */ @Override 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) { PivotUtilInternal.debugPrintln("Finalize " + getClass().getSimpleName() + "@" + Integer.toHexString(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. */ @Override 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) { @NonNull String projectName = platformURI.segment(1); getProjectDescriptors(); IProjectDescriptor projectDescriptor = project2descriptor.get(projectName); if (projectDescriptor == 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.domain => * file:/C:/GIT/org.eclipse.ocl/pivot/org.eclipse.ocl.domain/ * <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, @NonNull IProjectDescriptor> project2descriptor2 = project2descriptor; if (project2descriptor2 == null) { project2descriptor = project2descriptor2 = new HashMap<String, @NonNull 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; } /** * Return the names of all the projects/bundles. */ public @Nullable Set<String> getProjectNames() { Map<String, IProjectDescriptor> project2descriptor2 = getProjectDescriptors(); if (project2descriptor2 == null) { return null; } else { return project2descriptor2.keySet(); } } @Override public Notifier getTarget() { return null; } /** * 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. */ @Override public void initializeResourceSet(@Nullable ResourceSet resourceSet) { initializeURIResourceMap(resourceSet); initializePlatformResourceMap(false); initializeURIMap(resourceSet); initializeGenModelLocationMap(false); initializePackageRegistry(resourceSet); if (resourceSet != null) { List<Adapter> eAdapters = resourceSet.eAdapters(); if (!eAdapters.contains(this)) { 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); assert projectDescriptor != null; projectDescriptor.initializeURIMap(uriMap); } } @Override public boolean isAdapterForType(Object type) { return (type instanceof Class<?>) && ((Class<?>) type).isAssignableFrom(StandaloneProjectMap.class); } @Override public boolean isGlobal() { return isGlobal; } 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() + "!/"; @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 void setTarget(Notifier newTarget) {} @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(" => "); IProjectDescriptor projectDescriptor = project2descriptor.get(projectName); assert projectDescriptor != null; s.append(projectDescriptor.getLocationURI()); } } return s.toString(); } @Override 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. */ @Override 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 " + NameUtil.debugSimpleName(resourceLoadStatus.getPackageRegistry())); } resourceLoadStrategy.useGeneratedResource(resourceLoadStatus, resource); } } } }