mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
[resource storage] Introduce support for storing computed resources, so the computed state can be loaded quickly when needed. (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=453047)
Change-Id: I575c46c7e3fe89cb178adb26b02df74c25308df1
This commit is contained in:
parent
fdfe435236
commit
86ece9e580
25 changed files with 1296 additions and 11 deletions
|
@ -30,7 +30,9 @@ import org.eclipse.core.runtime.OperationCanceledException;
|
|||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.ui.actions.WorkspaceModifyOperation;
|
||||
import org.eclipse.ui.texteditor.MarkerUtilities;
|
||||
import org.eclipse.xtext.util.StringInputStream;
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
|
@ -53,6 +55,13 @@ public class IResourcesSetupUtil {
|
|||
project.open(monitor());
|
||||
return project;
|
||||
}
|
||||
|
||||
public static void assertNoErrorsInWorkspace() throws CoreException {
|
||||
IMarker[] findMarkers = ResourcesPlugin.getWorkspace().getRoot().findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
|
||||
for (IMarker iMarker : findMarkers) {
|
||||
Assert.assertFalse(MarkerUtilities.getMarkerType(iMarker)+"-"+MarkerUtilities.getMessage(iMarker), MarkerUtilities.getSeverity(iMarker) == IMarker.SEVERITY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addNature(IProject project, String nature)
|
||||
throws CoreException {
|
||||
|
|
|
@ -42,11 +42,19 @@ import org.eclipse.jdt.core.JavaCore;
|
|||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.internal.core.ClasspathEntry;
|
||||
import org.eclipse.jdt.internal.core.JavaModelManager;
|
||||
import org.eclipse.jdt.launching.IVMInstall;
|
||||
import org.eclipse.jdt.launching.IVMInstall2;
|
||||
import org.eclipse.jdt.launching.JavaRuntime;
|
||||
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
|
||||
import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;
|
||||
import org.eclipse.xtext.ui.util.JREContainerProvider;
|
||||
import org.eclipse.xtext.util.RuntimeIOException;
|
||||
import org.eclipse.xtext.xbase.lib.Pair;
|
||||
import org.osgi.service.prefs.BackingStoreException;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
/**
|
||||
* @author Jan Koehnlein - Initial contribution and API
|
||||
|
@ -85,6 +93,22 @@ public class JavaProjectSetupUtil {
|
|||
throw new WrappedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream jarInputStream(Pair<String, InputStream> ...entries) {
|
||||
try {
|
||||
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
|
||||
JarOutputStream jo = new JarOutputStream(new BufferedOutputStream(out2));
|
||||
for (Pair<String, InputStream> entry : entries) {
|
||||
JarEntry je = new JarEntry(entry.getKey());
|
||||
jo.putNextEntry(je);
|
||||
ByteStreams.copy(entry.getValue(), jo);
|
||||
}
|
||||
jo.close();
|
||||
return new ByteArrayInputStream(out2.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new WrappedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static IJavaProject createJavaProject(String projectName) throws CoreException {
|
||||
IProject project = createSimpleProject(projectName);
|
||||
|
@ -274,11 +298,38 @@ public class JavaProjectSetupUtil {
|
|||
}
|
||||
|
||||
public static void addJreClasspathEntry(IJavaProject javaProject) throws JavaModelException {
|
||||
// init default mappings
|
||||
makeJava7Default();
|
||||
IClasspathEntry existingJreContainerClasspathEntry = getJreContainerClasspathEntry(javaProject);
|
||||
if (existingJreContainerClasspathEntry == null) {
|
||||
addToClasspath(javaProject, JREContainerProvider.getDefaultJREContainerEntry());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean isJava7Default = false;
|
||||
|
||||
public static void makeJava7Default() {
|
||||
if (!isJava7Default) {
|
||||
IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager();
|
||||
IExecutionEnvironment[] environments = manager.getExecutionEnvironments();
|
||||
for (int i = 0; i < environments.length; i++) {
|
||||
IExecutionEnvironment environment = environments[i];
|
||||
if (environment.getId().equals("JavaSE-1.6") && environment.getDefaultVM() == null) {
|
||||
IVMInstall[] compatibleVMs = environment.getCompatibleVMs();
|
||||
for (IVMInstall ivmInstall : compatibleVMs) {
|
||||
if (ivmInstall instanceof IVMInstall2) {
|
||||
IVMInstall2 install2 = (IVMInstall2) ivmInstall;
|
||||
if (install2.getJavaVersion().startsWith("1.7")) {
|
||||
environment.setDefaultVM(ivmInstall);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
isJava7Default = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeJava5Compliant(IJavaProject javaProject) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
10
plugins/org.eclipse.xtext/.settings/.api_filters
Normal file
10
plugins/org.eclipse.xtext/.settings/.api_filters
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<component id="org.eclipse.xtext" version="2">
|
||||
<resource path="src/org/eclipse/xtext/resource/DerivedStateAwareResourceDescriptionManager.java" type="org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager">
|
||||
<filter id="338849923">
|
||||
<message_arguments>
|
||||
<message_argument value="org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager"/>
|
||||
</message_arguments>
|
||||
</filter>
|
||||
</resource>
|
||||
</component>
|
|
@ -61,6 +61,7 @@ Export-Package: org.eclipse.xtext,
|
|||
org.eclipse.xtext.resource.containers,
|
||||
org.eclipse.xtext.resource.generic,
|
||||
org.eclipse.xtext.resource.impl,
|
||||
org.eclipse.xtext.resource.persistence;x-friends:="org.eclipse.xtext.tests,org.eclipse.xtend.core,org.eclipse.xtend.core.tests",
|
||||
org.eclipse.xtext.scoping,
|
||||
org.eclipse.xtext.scoping.impl,
|
||||
org.eclipse.xtext.serializer,
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.eclipse.xtext.generator;
|
|||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
|
||||
/**
|
||||
*
|
||||
* Abstract base class for file system access supporting {@link IFileSystemAccessExtension3}.
|
||||
|
@ -16,7 +18,7 @@ import java.io.InputStream;
|
|||
* @author Sven Efftinge - Initial contribution and API
|
||||
* @since 2.4
|
||||
*/
|
||||
public abstract class AbstractFileSystemAccess2 extends AbstractFileSystemAccess implements IFileSystemAccessExtension3{
|
||||
public abstract class AbstractFileSystemAccess2 extends AbstractFileSystemAccess implements IFileSystemAccessExtension3 {
|
||||
|
||||
/**
|
||||
* @since 2.4
|
||||
|
@ -43,4 +45,16 @@ public abstract class AbstractFileSystemAccess2 extends AbstractFileSystemAccess
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the context to further configure this file system access instance.
|
||||
*
|
||||
* @param context - a context from which project configuration can be obtained. Supported context types
|
||||
* depend on the concrete implementation, but {@link Resource} is usually a good fit.
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
public void setContext(Object context) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.generator;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
@ImplementedBy(OutputConfigurationProvider.class)
|
||||
public interface IContextualOutputConfigurationProvider {
|
||||
|
||||
Set<OutputConfiguration> getOutputConfigurations(Resource context);
|
||||
}
|
|
@ -9,6 +9,8 @@ package org.eclipse.xtext.generator;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +21,7 @@ import com.google.inject.ImplementedBy;
|
|||
public interface IOutputConfigurationProvider {
|
||||
Set<OutputConfiguration> getOutputConfigurations();
|
||||
|
||||
class Delegate implements IOutputConfigurationProvider {
|
||||
class Delegate implements IOutputConfigurationProvider, IContextualOutputConfigurationProvider {
|
||||
private IOutputConfigurationProvider delegate;
|
||||
|
||||
public IOutputConfigurationProvider getDelegate() {
|
||||
|
@ -36,5 +38,15 @@ public interface IOutputConfigurationProvider {
|
|||
return delegate.getOutputConfigurations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
public Set<OutputConfiguration> getOutputConfigurations(Resource context) {
|
||||
if (delegate instanceof IContextualOutputConfigurationProvider) {
|
||||
return ((IContextualOutputConfigurationProvider) delegate).getOutputConfigurations(context);
|
||||
}
|
||||
return delegate.getOutputConfigurations();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@ import static com.google.common.collect.Sets.*;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
* @since 2.1
|
||||
*/
|
||||
public class OutputConfigurationProvider implements IOutputConfigurationProvider {
|
||||
public class OutputConfigurationProvider implements IOutputConfigurationProvider, IContextualOutputConfigurationProvider {
|
||||
|
||||
/**
|
||||
* @return a set of {@link OutputConfiguration} available for the generator
|
||||
|
@ -32,4 +34,12 @@ public class OutputConfigurationProvider implements IOutputConfigurationProvider
|
|||
defaultOutput.setKeepLocalHistory(true);
|
||||
return newHashSet(defaultOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
@Override
|
||||
public Set<OutputConfiguration> getOutputConfigurations(Resource context) {
|
||||
return getOutputConfigurations();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ import org.eclipse.emf.common.util.EList;
|
|||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.InternalEObject;
|
||||
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
|
||||
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
|
||||
import org.eclipse.xtext.parser.IParseResult;
|
||||
import org.eclipse.xtext.resource.persistence.StorageAwareResource;
|
||||
import org.eclipse.xtext.util.IResourceScopeCache;
|
||||
import org.eclipse.xtext.util.OnChangeEvictingCache;
|
||||
|
||||
|
@ -28,7 +28,7 @@ import com.google.inject.Inject;
|
|||
* @author Sven Efftinge - Initial contribution and API
|
||||
* @since 2.1
|
||||
*/
|
||||
public class DerivedStateAwareResource extends LazyLinkingResource {
|
||||
public class DerivedStateAwareResource extends StorageAwareResource {
|
||||
|
||||
@Inject(optional=true)
|
||||
private IDerivedStateComputer derivedStateComputer;
|
||||
|
@ -62,7 +62,7 @@ public class DerivedStateAwareResource extends LazyLinkingResource {
|
|||
*/
|
||||
@Override
|
||||
public synchronized EList<EObject> getContents() {
|
||||
if (isLoaded && !isLoading && !isInitializing && !isUpdating && !fullyInitialized) {
|
||||
if (isLoaded && !isLoading && !isInitializing && !isUpdating && !fullyInitialized && !isLoadedFromStorage()) {
|
||||
try {
|
||||
eSetDeliver(false);
|
||||
installDerivedState(false);
|
||||
|
@ -109,6 +109,7 @@ public class DerivedStateAwareResource extends LazyLinkingResource {
|
|||
unloaded((InternalEObject) allContents.next());
|
||||
}
|
||||
setParseResult(null);
|
||||
setIsLoadedFromStorage(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +190,7 @@ public class DerivedStateAwareResource extends LazyLinkingResource {
|
|||
public void installDerivedState(boolean preIndexingPhase) {
|
||||
if (!isLoaded)
|
||||
throw new IllegalStateException("The resource must be loaded, before installDerivedState can be called.");
|
||||
if (!fullyInitialized && !isInitializing) {
|
||||
if (!fullyInitialized && !isInitializing && !isLoadedFromStorage()) {
|
||||
try {
|
||||
isInitializing = true;
|
||||
if (derivedStateComputer != null)
|
||||
|
|
|
@ -12,8 +12,8 @@ import java.io.IOException;
|
|||
import org.apache.log4j.Logger;
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
import org.eclipse.xtext.resource.impl.DefaultResourceDescription;
|
||||
import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionManager;
|
||||
import org.eclipse.xtext.resource.impl.EObjectDescriptionLookUp;
|
||||
import org.eclipse.xtext.resource.persistence.StorageAwareResourceDescriptionManager;
|
||||
import org.eclipse.xtext.util.IResourceScopeCache;
|
||||
import org.eclipse.xtext.util.RuntimeIOException;
|
||||
|
||||
|
@ -28,7 +28,7 @@ import com.google.inject.Singleton;
|
|||
* @since 2.1
|
||||
*/
|
||||
@Singleton
|
||||
public class DerivedStateAwareResourceDescriptionManager extends DefaultResourceDescriptionManager {
|
||||
public class DerivedStateAwareResourceDescriptionManager extends StorageAwareResourceDescriptionManager {
|
||||
|
||||
private final static Logger log = Logger.getLogger(DerivedStateAwareResourceDescriptionManager.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource;
|
||||
|
||||
import org.eclipse.emf.common.util.URI;
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @noimplement This interface is not intended to be implemented by clients.
|
||||
* @since 2.8
|
||||
*/
|
||||
public interface IResourceServiceProviderExtension {
|
||||
|
||||
public boolean isReadOnly(URI uri);
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.eclipse.xtext.resource.FileExtensionProvider;
|
|||
import org.eclipse.xtext.resource.IContainer;
|
||||
import org.eclipse.xtext.resource.IResourceDescription;
|
||||
import org.eclipse.xtext.resource.IResourceServiceProvider;
|
||||
import org.eclipse.xtext.resource.IResourceServiceProviderExtension;
|
||||
import org.eclipse.xtext.validation.IResourceValidator;
|
||||
|
||||
import com.google.inject.ConfigurationException;
|
||||
|
@ -22,7 +23,7 @@ import com.google.inject.Injector;
|
|||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
public class DefaultResourceServiceProvider implements IResourceServiceProvider {
|
||||
public class DefaultResourceServiceProvider implements IResourceServiceProvider, IResourceServiceProviderExtension {
|
||||
|
||||
@Inject
|
||||
private IContainer.Manager containerManager;
|
||||
|
@ -75,5 +76,13 @@ public class DefaultResourceServiceProvider implements IResourceServiceProvider
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadOnly(URI uri) {
|
||||
return uri.isArchive();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import org.eclipse.xtext.generator.IFileSystemAccessExtension3
|
||||
import java.io.OutputStream
|
||||
import java.io.InputStream
|
||||
import org.eclipse.emf.common.util.URI
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @noimplement
|
||||
* @noextend
|
||||
* @since 2.8
|
||||
*/
|
||||
interface IResourceStorageFacade {
|
||||
|
||||
/**
|
||||
* @return whether the given resource should and can be loaded from stored resource state
|
||||
*/
|
||||
def boolean shouldLoadFromStorage(StorageAwareResource resource)
|
||||
|
||||
/**
|
||||
* @return whether storage data exists for the given URI
|
||||
*/
|
||||
def boolean hasStorageFor(URI uri)
|
||||
|
||||
/**
|
||||
* Finds or creates a ResourceStorageLoadable for the given resource.
|
||||
* Clients should first call shouldLoadFromStorage to check whether there exists a storage version
|
||||
* of the given resource.
|
||||
*
|
||||
* @return an IResourceStorageLoadable
|
||||
*/
|
||||
def ResourceStorageLoadable getOrCreateResourceStorageLoadable(StorageAwareResource resource)
|
||||
|
||||
/**
|
||||
* Saves the resource using the given file system access.
|
||||
*/
|
||||
def void saveResource(StorageAwareResource resource, IFileSystemAccessExtension3 fsa)
|
||||
|
||||
/**
|
||||
* Creates a fresh ResourceStorageWritable wrapping the given OutputStream
|
||||
*/
|
||||
def ResourceStorageWritable createResourceStorageWritable(OutputStream outputStream)
|
||||
|
||||
/**
|
||||
* Creates a fresh ResourceStorageLoadable wrapping the given InputStream
|
||||
*/
|
||||
def ResourceStorageLoadable createResourceStorageLoadable(InputStream inputStream)
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import com.google.common.base.Predicates
|
||||
import com.google.common.base.Splitter
|
||||
import com.google.inject.Inject
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.EObject
|
||||
import org.eclipse.emf.ecore.EcoreFactory
|
||||
import org.eclipse.emf.ecore.EcorePackage
|
||||
import org.eclipse.emf.ecore.InternalEObject
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil
|
||||
import org.eclipse.xtend.lib.annotations.Data
|
||||
import org.eclipse.xtext.naming.QualifiedName
|
||||
import org.eclipse.xtext.resource.IEObjectDescription
|
||||
import org.eclipse.xtext.resource.XtextResource
|
||||
import org.eclipse.xtext.scoping.IGlobalScopeProvider
|
||||
import org.eclipse.xtext.linking.lazy.LazyURIEncoder
|
||||
|
||||
/**
|
||||
* Portable URIs are based on names and therefore are independent of the concrete file pathes and fuile names the
|
||||
* of resources.
|
||||
*
|
||||
* A portable URI is really a resource URI to the client URI and a fragment that contains the information to retrieve the
|
||||
* referenced element using the global scoping. That is it contains
|
||||
* <ul>
|
||||
* <li>the qualified name of a container of the target element
|
||||
* <li>the type of that container
|
||||
* <li>the path from that container to the actual target element
|
||||
* </ul>
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
class PortableURIs {
|
||||
|
||||
public static val PORTABLE_SCHEME = "portable"
|
||||
|
||||
@Inject IGlobalScopeProvider globalScopeProvider
|
||||
@Inject LazyURIEncoder lazyURIencoder
|
||||
|
||||
def boolean isPortableURIFragment(String uriFragment) {
|
||||
uriFragment.startsWith(PORTABLE_SCHEME)
|
||||
}
|
||||
|
||||
def EObject resolve(StorageAwareResource resource, String portableFragment) {
|
||||
val desc = fromFragmentString(portableFragment)
|
||||
val mock = EcoreFactory.eINSTANCE.createEReference
|
||||
mock.EType = EcorePackage.Literals.EOBJECT
|
||||
val scope = globalScopeProvider.getScope(resource, mock, Predicates.alwaysTrue)
|
||||
val description = scope.getElements(desc.descriptionQualifiedName).findFirst[
|
||||
EClass.name == desc.descriptionEClassName
|
||||
]
|
||||
if (description == null) {
|
||||
return null
|
||||
}
|
||||
val container = EcoreUtil.resolve(description.EObjectOrProxy, resource)
|
||||
return getEObject(container, desc.descriptionRelativeFragment)
|
||||
}
|
||||
|
||||
def URI toPortableURI(StorageAwareResource res, URI uri, String fragment) {
|
||||
if (res.URI == uri && lazyURIencoder.isCrossLinkFragment(res, fragment)) {
|
||||
// try resolve
|
||||
val result = res.getEObject(fragment)
|
||||
if (result==null || result.eIsProxy) {
|
||||
//this means it is an unresolvable lazy link, we won't be able to resolve it later
|
||||
return uri.appendFragment(StorageAwareResource.UNRESOLVABLE_FRAGMENT)
|
||||
} else {
|
||||
val portableFragment = getPortableURIFragment(result)
|
||||
if (portableFragment != null) {
|
||||
return res.URI.appendFragment(portableFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
val resource = res.resourceSet.getResource(uri, false)
|
||||
if (resource != null) {
|
||||
val obj = resource.getEObject(fragment)
|
||||
if (obj != null) {
|
||||
val portableFragment = getPortableURIFragment(obj)
|
||||
if (portableFragment != null) {
|
||||
return res.URI.appendFragment(portableFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
return uri.appendFragment(fragment)
|
||||
}
|
||||
|
||||
protected def String getPortableURIFragment(EObject obj) {
|
||||
switch res : obj.eResource {
|
||||
XtextResource : {
|
||||
val desc = res.resourceServiceProvider.resourceDescriptionManager.getResourceDescription(obj.eResource)
|
||||
val containerDesc = desc.exportedObjects.findFirst [
|
||||
val possibleContainer = EcoreUtil.resolve(EObjectOrProxy, res)
|
||||
obj==possibleContainer || EcoreUtil.isAncestor(obj, possibleContainer)
|
||||
]
|
||||
if (containerDesc != null) {
|
||||
val fragment = createPortableURIFragment(containerDesc, obj)
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
protected def String createPortableURIFragment(IEObjectDescription desc, EObject target) {
|
||||
val possibleContainer = EcoreUtil.resolve(desc.EObjectOrProxy, target)
|
||||
val fragmentToTarget = getFragment(target, possibleContainer)
|
||||
val portableDescription = new PortableFragmentDescription(desc.EClass.name, desc.qualifiedName, fragmentToTarget)
|
||||
return toFragmentString(portableDescription)
|
||||
}
|
||||
|
||||
protected def String toFragmentString(PortableFragmentDescription desc) {
|
||||
val typeName = desc.descriptionEClassName
|
||||
val segments = desc.descriptionQualifiedName.segments
|
||||
//TODO use safe delimiter algorithm (library)
|
||||
var uriFragment = PORTABLE_SCHEME + '#' + typeName + '#'+ segments.join(':')
|
||||
if (desc.descriptionRelativeFragment != null) {
|
||||
uriFragment += '#' + desc.descriptionRelativeFragment
|
||||
}
|
||||
return uriFragment
|
||||
}
|
||||
|
||||
protected def PortableFragmentDescription fromFragmentString(String fragmentString) {
|
||||
val segments = Splitter.on('#').split(fragmentString).iterator
|
||||
segments.next // skip first
|
||||
val eClassName = segments.next
|
||||
val qname = QualifiedName.create(Splitter.on(':').split(segments.next).toList)
|
||||
val fragment = if (segments.hasNext) {
|
||||
segments.next
|
||||
}
|
||||
return new PortableFragmentDescription(eClassName, qname, fragment)
|
||||
}
|
||||
|
||||
protected def String getFragment(EObject fromContainer, EObject toChild) {
|
||||
if (fromContainer == toChild)
|
||||
return null
|
||||
var lastChild = toChild as InternalEObject
|
||||
var lastContainer = lastChild.eInternalContainer
|
||||
var result = lastContainer.eURIFragmentSegment(lastChild.eContainingFeature, lastChild)
|
||||
while (lastContainer != null && fromContainer != lastContainer) {
|
||||
lastChild = lastContainer
|
||||
lastContainer = lastContainer.eInternalContainer
|
||||
if (lastContainer == null) {
|
||||
throw new IllegalStateException("No more containers for element "+lastChild)
|
||||
}
|
||||
result = lastContainer.eURIFragmentSegment(lastChild.eContainingFeature, lastChild)+'/'+result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
protected def EObject getEObject(EObject from, String toFragment) {
|
||||
if (toFragment == null)
|
||||
return from
|
||||
val splitted = Splitter.on("/").split(toFragment)
|
||||
return splitted.fold(from) [
|
||||
($0 as InternalEObject).eObjectForURIFragmentSegment($1)
|
||||
]
|
||||
}
|
||||
|
||||
@Data static class PortableFragmentDescription {
|
||||
String descriptionEClassName
|
||||
QualifiedName descriptionQualifiedName
|
||||
String descriptionRelativeFragment
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import com.google.inject.Inject
|
||||
import com.google.inject.Provider
|
||||
import org.eclipse.xtext.generator.AbstractFileSystemAccess2
|
||||
import org.eclipse.xtext.generator.IContextualOutputConfigurationProvider
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import org.eclipse.xtext.generator.IFileSystemAccessExtension3
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class ResourceStorageFacade implements IResourceStorageFacade {
|
||||
|
||||
@Inject IContextualOutputConfigurationProvider outputConfigurationProvider
|
||||
@Inject Provider<AbstractFileSystemAccess2> fileSystemAccessProvider
|
||||
|
||||
/**
|
||||
* @return whether the given resource should be loaded from stored resource state
|
||||
*/
|
||||
override boolean shouldLoadFromStorage(StorageAwareResource resource) {
|
||||
val adapter = SourceLevelURIsAdapter.findInstalledAdapter(resource.resourceSet)
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
} else {
|
||||
if (adapter.sourceLevelURIs.contains(resource.URI))
|
||||
return false;
|
||||
}
|
||||
return doesStorageExist(resource)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a ResourceStorageLoadable for the given resource.
|
||||
* Clients should first call shouldLoadFromStorage to check whether there exists a storage version
|
||||
* of the given resource.
|
||||
*
|
||||
* @return an IResourceStorageLoadable
|
||||
*/
|
||||
override ResourceStorageLoadable getOrCreateResourceStorageLoadable(StorageAwareResource resource) {
|
||||
val stateProvider = resource.resourceSet.eAdapters.filter(ResourceStorageProviderAdapter).head
|
||||
if (stateProvider != null) {
|
||||
val inputStream = stateProvider.getResourceStorageLoadable(resource)
|
||||
if (inputStream != null)
|
||||
return inputStream
|
||||
}
|
||||
val inputStream = if (resource.resourceSet.URIConverter.exists(resource.URI.getBinaryStrorageURI, emptyMap)) {
|
||||
resource.resourceSet.URIConverter.createInputStream(resource.URI.getBinaryStrorageURI)
|
||||
} else {
|
||||
val fsa = getFileSystemAccess(resource);
|
||||
val outputRelativePath = computeOutputPath(resource)
|
||||
fsa.readBinaryFile(outputRelativePath)
|
||||
}
|
||||
return createResourceStorageLoadable(inputStream)
|
||||
}
|
||||
|
||||
override void saveResource(StorageAwareResource resource, IFileSystemAccessExtension3 fsa) {
|
||||
val path = computeOutputPath(resource)
|
||||
val bout = new MyByteArrayOutputStream()
|
||||
val outStream = createResourceStorageWritable(bout)
|
||||
outStream.writeResource(resource)
|
||||
fsa.generateFile(path, new ByteArrayInputStream(bout.toByteArray, 0, bout.length))
|
||||
}
|
||||
|
||||
override def ResourceStorageLoadable createResourceStorageLoadable(InputStream in) {
|
||||
return new ResourceStorageLoadable(in)
|
||||
}
|
||||
|
||||
override def ResourceStorageWritable createResourceStorageWritable(OutputStream out) {
|
||||
return new ResourceStorageWritable(out)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether a stored resource state exists for the given resource
|
||||
*/
|
||||
protected def doesStorageExist(StorageAwareResource resource) {
|
||||
val stateProvider = resource.resourceSet.eAdapters.filter(ResourceStorageProviderAdapter).head
|
||||
if (stateProvider!=null && stateProvider.getResourceStorageLoadable(resource) != null)
|
||||
return true;
|
||||
// check for next to original location, i.e. jars
|
||||
if (resource.resourceSet.URIConverter.exists(resource.URI.getBinaryStrorageURI, emptyMap)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// check for source project locations, i.e. use generator config
|
||||
val fsa = getFileSystemAccess(resource);
|
||||
val outputRelativePath = computeOutputPath(resource)
|
||||
val uri = fsa.getURI(outputRelativePath)
|
||||
return resource.resourceSet.URIConverter.exists(uri, null)
|
||||
}
|
||||
|
||||
protected def getFileSystemAccess(StorageAwareResource resource) {
|
||||
val fsa = fileSystemAccessProvider.get()
|
||||
fsa.context = resource
|
||||
fsa.outputConfigurations = outputConfigurationProvider.getOutputConfigurations(resource).toMap[name]
|
||||
return fsa
|
||||
}
|
||||
|
||||
protected def computeOutputPath(StorageAwareResource resource) {
|
||||
val uri = resource.URI.getBinaryStrorageURI
|
||||
val srcFolderPath = uri.trimFileExtension.trimSegments(uri.segmentCount-3).toString
|
||||
val outputRelativePath = uri.toString.substring(srcFolderPath.length+1)
|
||||
return outputRelativePath
|
||||
}
|
||||
|
||||
override hasStorageFor(URI uri) {
|
||||
new ExtensibleURIConverterImpl().exists(getBinaryStrorageURI(uri), emptyMap())
|
||||
}
|
||||
|
||||
protected def getBinaryStrorageURI(URI sourceURI) {
|
||||
return sourceURI.trimSegments(1).appendSegment("."+sourceURI.lastSegment+'bin')
|
||||
}
|
||||
|
||||
private static class MyByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
override toByteArray() { buf }
|
||||
def int length() { count }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
import org.apache.log4j.Logger
|
||||
import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl
|
||||
import org.eclipse.xtend.lib.annotations.Data
|
||||
import java.io.ObjectInputStream
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
@Data class ResourceStorageLoadable {
|
||||
|
||||
static val LOG = Logger.getLogger(ResourceStorageLoadable)
|
||||
|
||||
InputStream in
|
||||
|
||||
protected def void loadIntoResource(StorageAwareResource resource) {
|
||||
try {
|
||||
if (!resource.isLoadedFromStorage) {
|
||||
throw new IllegalStateException("Please use StorageAwareResource#load(ResourceStorageLoadable).");
|
||||
}
|
||||
val zin = new ZipInputStream(in)
|
||||
try {
|
||||
loadEntries(resource, zin)
|
||||
} finally {
|
||||
zin.close
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Problem loading storage for "+resource.URI+". Error was:"+e.message, e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load entries from the storage.
|
||||
* Overriding methods should first delegate to super before adding their own entries.
|
||||
*/
|
||||
protected def void loadEntries(StorageAwareResource resource, ZipInputStream zipIn) {
|
||||
readContents(resource, zipIn)
|
||||
readResourceDescription(resource, zipIn)
|
||||
}
|
||||
|
||||
protected def void readResourceDescription(StorageAwareResource resource, ZipInputStream zipIn) {
|
||||
zipIn.nextEntry
|
||||
val objectIn = new ObjectInputStream(zipIn)
|
||||
val description = objectIn.readObject as SerializableResourceDescription
|
||||
description.updateResourceURI(resource.URI)
|
||||
resource.resourceDescription = description
|
||||
}
|
||||
|
||||
protected def void readContents(StorageAwareResource resource, ZipInputStream zipIn) {
|
||||
zipIn.nextEntry
|
||||
val in = new BinaryResourceImpl.EObjectInputStream(zipIn, emptyMap) {
|
||||
|
||||
override readCompressedInt() throws IOException {
|
||||
//HACK! null resource set, to avoid usage of resourceSet's package registry
|
||||
resourceSet = null
|
||||
super.readCompressedInt()
|
||||
}
|
||||
|
||||
}
|
||||
in.loadResource(resource)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import org.eclipse.emf.common.notify.impl.AdapterImpl
|
||||
|
||||
/**
|
||||
* An adapter that can be installed into a SerializableResource,
|
||||
* to provide resource state. It is used with dirty editors providing the dirty non persisted
|
||||
* state to other editors.
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class ResourceStorageProviderAdapter extends AdapterImpl {
|
||||
|
||||
override isAdapterForType(Object type) {
|
||||
type == ResourceStorageProviderAdapter
|
||||
}
|
||||
|
||||
def ResourceStorageLoadable getResourceStorageLoadable(StorageAwareResource resource) {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import org.apache.log4j.Logger
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl
|
||||
import org.eclipse.xtend.lib.annotations.Data
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
@Data class ResourceStorageWritable {
|
||||
|
||||
static val LOG = Logger.getLogger(ResourceStorageWritable)
|
||||
|
||||
OutputStream out
|
||||
|
||||
def void writeResource(StorageAwareResource resource) {
|
||||
if (resource.isLoadedFromStorage) {
|
||||
throw new IllegalStateException("cannot write resources loaded from storage. URI was "+resource.URI)
|
||||
}
|
||||
val zipOut = new ZipOutputStream(out)
|
||||
try {
|
||||
writeEntries(resource, zipOut)
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.message, e)
|
||||
} finally {
|
||||
zipOut.close
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write entries into the storage.
|
||||
* Overriding methods should first delegate to super before adding their own entries.
|
||||
*/
|
||||
protected def void writeEntries(StorageAwareResource resource, ZipOutputStream zipOut) {
|
||||
writeContents(resource, zipOut)
|
||||
writeResourceDescription(resource, zipOut)
|
||||
}
|
||||
|
||||
protected def void writeContents(StorageAwareResource storageAwareResource, ZipOutputStream zipOut) {
|
||||
zipOut.putNextEntry(new ZipEntry("emf-contents"))
|
||||
val out = new BinaryResourceImpl.EObjectOutputStream(zipOut, emptyMap) {
|
||||
override writeURI(URI uri, String fragment) throws IOException {
|
||||
val portableURI = storageAwareResource.portableURIs.toPortableURI(storageAwareResource, uri, fragment)
|
||||
super.writeURI(portableURI.trimFragment, portableURI.fragment)
|
||||
}
|
||||
}
|
||||
try {
|
||||
out.saveResource(storageAwareResource)
|
||||
} finally {
|
||||
out.flush
|
||||
}
|
||||
zipOut.closeEntry
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected def void writeResourceDescription(StorageAwareResource resource, ZipOutputStream zipOut) {
|
||||
zipOut.putNextEntry(new ZipEntry("resource-description"))
|
||||
val description = resource.resourceServiceProvider.resourceDescriptionManager.getResourceDescription(resource);
|
||||
val serializableDescription = SerializableResourceDescription.createCopy(description)
|
||||
val out = new ObjectOutputStream(zipOut);
|
||||
try {
|
||||
out.writeObject(serializableDescription);
|
||||
} finally {
|
||||
out.flush
|
||||
}
|
||||
zipOut.closeEntry
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import java.io.Externalizable
|
||||
import java.io.IOException
|
||||
import java.io.ObjectInput
|
||||
import java.io.ObjectOutput
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import java.util.List
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.EClass
|
||||
import org.eclipse.emf.ecore.ENamedElement
|
||||
import org.eclipse.emf.ecore.EObject
|
||||
import org.eclipse.emf.ecore.EPackage
|
||||
import org.eclipse.emf.ecore.EReference
|
||||
import org.eclipse.emf.ecore.InternalEObject
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
import org.eclipse.xtext.naming.QualifiedName
|
||||
import org.eclipse.xtext.resource.IEObjectDescription
|
||||
import org.eclipse.xtext.resource.IReferenceDescription
|
||||
import org.eclipse.xtext.resource.IResourceDescription
|
||||
import org.eclipse.xtext.resource.impl.AbstractResourceDescription
|
||||
|
||||
import static extension org.eclipse.xtext.resource.persistence.SerializationExtensions.*
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
@Accessors class SerializableResourceDescription extends AbstractResourceDescription implements Externalizable {
|
||||
|
||||
def static SerializableResourceDescription createCopy(IResourceDescription desc) {
|
||||
new SerializableResourceDescription => [
|
||||
URI = desc.URI
|
||||
descriptions = desc.exportedObjects.map[createCopy(it)].toList
|
||||
references = desc.referenceDescriptions.map[createCopy(it)].toList
|
||||
importedNames = newArrayList(desc.importedNames)
|
||||
]
|
||||
}
|
||||
|
||||
private def static SerializableEObjectDescription createCopy(IEObjectDescription desc) {
|
||||
new SerializableEObjectDescription => [
|
||||
EClass = desc.EClass
|
||||
EObjectURI = desc.EObjectURI
|
||||
qualifiedName = desc.qualifiedName
|
||||
userData = new HashMap(desc.userDataKeys.size)
|
||||
for (key : desc.userDataKeys) {
|
||||
userData.put(key, desc.getUserData(key))
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
private def static SerializableReferenceDescription createCopy(IReferenceDescription desc) {
|
||||
new SerializableReferenceDescription => [
|
||||
sourceEObjectUri = desc.sourceEObjectUri
|
||||
targetEObjectUri = desc.targetEObjectUri
|
||||
EReference = desc.EReference
|
||||
indexInList = desc.indexInList
|
||||
containerEObjectURI = desc.containerEObjectURI
|
||||
]
|
||||
}
|
||||
|
||||
List<SerializableEObjectDescription> descriptions = emptyList
|
||||
List<SerializableReferenceDescription> references = emptyList
|
||||
List<QualifiedName> importedNames = emptyList
|
||||
URI uRI
|
||||
|
||||
def void updateResourceURI(URI uri) {
|
||||
this.uRI = uri
|
||||
for (desc : descriptions) {
|
||||
desc.updateResourceURI(uri)
|
||||
}
|
||||
}
|
||||
|
||||
override protected computeExportedObjects() {
|
||||
descriptions as List<?> as List<IEObjectDescription>
|
||||
}
|
||||
|
||||
override getImportedNames() {
|
||||
importedNames
|
||||
}
|
||||
|
||||
override getReferenceDescriptions() {
|
||||
references as Iterable<?> as Iterable<IReferenceDescription>
|
||||
}
|
||||
|
||||
override readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
URI = in.readURI
|
||||
val descriptionsSize = in.readInt
|
||||
descriptions = new ArrayList(descriptionsSize);
|
||||
for (i : 0 ..< descriptionsSize) {
|
||||
descriptions.add(in.readCastedObject)
|
||||
}
|
||||
//reference descriptions are not portable atm.
|
||||
// val referencesSize = in.readInt
|
||||
// references = new ArrayList(referencesSize);
|
||||
// for (i : 0 ..< referencesSize) {
|
||||
// references.add(in.readCastedObject)
|
||||
// }
|
||||
val importedNamesSize = in.readInt
|
||||
importedNames = new ArrayList(importedNamesSize)
|
||||
for (i : 0 ..< importedNamesSize) {
|
||||
importedNames.add(in.readQualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
override writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeURI(uRI)
|
||||
out.writeInt(descriptions.size)
|
||||
for (desc : descriptions) {
|
||||
out.writeObject(desc)
|
||||
}
|
||||
//reference descriptions are not portable atm.
|
||||
// out.writeInt(references.size)
|
||||
// for (ref : references) {
|
||||
// out.writeObject(ref)
|
||||
// }
|
||||
out.writeInt(importedNames.size)
|
||||
for (name : importedNames) {
|
||||
out.writeQualifiedName(name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
@Accessors class SerializableEObjectDescription implements IEObjectDescription, Externalizable {
|
||||
|
||||
URI eObjectURI
|
||||
EClass eClass
|
||||
QualifiedName qualifiedName
|
||||
HashMap<String,String> userData
|
||||
@Accessors(NONE) transient EObject eObjectOrProxy
|
||||
|
||||
def void updateResourceURI(URI uri) {
|
||||
eObjectURI = uri.appendFragment(eObjectURI.fragment)
|
||||
}
|
||||
|
||||
override getEObjectOrProxy() {
|
||||
if (eObjectOrProxy == null) {
|
||||
val proxy = EcoreUtil.create(eClass)
|
||||
(proxy as InternalEObject).eSetProxyURI(eObjectURI)
|
||||
eObjectOrProxy = proxy
|
||||
}
|
||||
return eObjectOrProxy
|
||||
}
|
||||
|
||||
override getName() {
|
||||
qualifiedName
|
||||
}
|
||||
|
||||
override getUserData(String key) {
|
||||
userData.get(key)
|
||||
}
|
||||
|
||||
override getUserDataKeys() {
|
||||
userData.keySet
|
||||
}
|
||||
|
||||
override readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
eObjectURI = in.readURI
|
||||
eClass = in.readEcoreElement
|
||||
qualifiedName = in.readQualifiedName
|
||||
userData = in.readCastedObject
|
||||
}
|
||||
|
||||
override writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeURI(eObjectURI)
|
||||
out.writeURI(EcoreUtil.getURI(eClass))
|
||||
out.writeQualifiedName(qualifiedName)
|
||||
out.writeObject(userData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
@Accessors class SerializableReferenceDescription implements IReferenceDescription, Externalizable {
|
||||
URI sourceEObjectUri
|
||||
URI targetEObjectUri
|
||||
URI containerEObjectURI
|
||||
EReference eReference
|
||||
int indexInList
|
||||
|
||||
override readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
sourceEObjectUri = in.readURI
|
||||
targetEObjectUri = in.readURI
|
||||
containerEObjectURI = in.readURI
|
||||
eReference = in.readEcoreElement
|
||||
indexInList = in.readInt
|
||||
}
|
||||
|
||||
override writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeURI(sourceEObjectUri)
|
||||
out.writeURI(targetEObjectUri)
|
||||
out.writeURI(containerEObjectURI)
|
||||
out.writeEcoreElement(eReference)
|
||||
out.writeInt(indexInList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.8
|
||||
*/
|
||||
package class SerializationExtensions {
|
||||
|
||||
def static <T extends ENamedElement> T readEcoreElement(ObjectInput in) {
|
||||
val uri = in.readURI
|
||||
val ePackage = EPackage.Registry.INSTANCE.getEPackage(uri.trimFragment.toString)
|
||||
return ePackage.eResource.getEObject(uri.fragment) as T
|
||||
}
|
||||
|
||||
def static void writeEcoreElement(ObjectOutput out, ENamedElement namedElement) {
|
||||
val uri = EcoreUtil.getURI(namedElement)
|
||||
out.writeURI(uri)
|
||||
}
|
||||
|
||||
def static <T> T readCastedObject(ObjectInput in) {
|
||||
in.readObject as T
|
||||
}
|
||||
|
||||
def static URI readURI(ObjectInput in) {
|
||||
return URI::createURI(in.readUTF)
|
||||
}
|
||||
|
||||
def static void writeURI(ObjectOutput out, URI uri) {
|
||||
out.writeUTF(uri.toString)
|
||||
}
|
||||
|
||||
def static QualifiedName readQualifiedName(ObjectInput in) {
|
||||
return QualifiedName.create(in.readObject as ArrayList<String>)
|
||||
}
|
||||
|
||||
def static void writeQualifiedName(ObjectOutput out, QualifiedName name) {
|
||||
out.writeObject(new ArrayList(name.segments))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import java.util.Collection
|
||||
import org.eclipse.emf.common.notify.impl.AdapterImpl
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.resource.ResourceSet
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
|
||||
/**
|
||||
* An adapter to be installed into a ResourceSet.
|
||||
*
|
||||
* It's used as a protocol to tell whether a StorageAwareResource
|
||||
* should load from source or could load from serialized data.
|
||||
*
|
||||
* @see ResourceStorageProviderAdapter
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
class SourceLevelURIsAdapter extends AdapterImpl {
|
||||
|
||||
@Accessors ImmutableSet<URI> sourceLevelURIs
|
||||
|
||||
override isAdapterForType(Object type) {
|
||||
return type == SourceLevelURIsAdapter
|
||||
}
|
||||
|
||||
def static void setSourceLevelUris(ResourceSet resourceSet, Collection<URI> uris) {
|
||||
val adapter = findInstalledAdapter(resourceSet)
|
||||
?: (new SourceLevelURIsAdapter => [
|
||||
resourceSet.eAdapters += it
|
||||
])
|
||||
adapter.sourceLevelURIs = ImmutableSet.copyOf(uris)
|
||||
}
|
||||
|
||||
def static SourceLevelURIsAdapter findInstalledAdapter(ResourceSet resourceSet) {
|
||||
resourceSet.eAdapters.filter(SourceLevelURIsAdapter).head
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import com.google.inject.Inject
|
||||
import java.io.IOException
|
||||
import java.util.Map
|
||||
import org.apache.log4j.Logger
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
import org.eclipse.xtext.linking.lazy.LazyLinkingResource
|
||||
import org.eclipse.xtext.resource.IResourceDescription
|
||||
import org.eclipse.xtext.util.internal.Stopwatches
|
||||
|
||||
/**
|
||||
* A resource implementation that can load itself from ResourceStorage.
|
||||
*
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class StorageAwareResource extends LazyLinkingResource {
|
||||
public static val UNRESOLVABLE_FRAGMENT = "UNRESOLVABLE"
|
||||
static val Logger LOG = Logger.getLogger(StorageAwareResource)
|
||||
|
||||
@Accessors(PUBLIC_GETTER) @Inject(optional=true) IResourceStorageFacade resourceStorageFacade
|
||||
|
||||
@Accessors(PUBLIC_GETTER) @Inject PortableURIs portableURIs
|
||||
|
||||
@Accessors boolean isLoadedFromStorage = false;
|
||||
|
||||
@Accessors IResourceDescription resourceDescription = null;
|
||||
|
||||
override load(Map<?, ?> options) throws IOException {
|
||||
if (!isLoaded && !isLoading && resourceStorageFacade!=null && resourceStorageFacade.shouldLoadFromStorage(this)) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("Loading "+URI+" from storage.")
|
||||
}
|
||||
val in = resourceStorageFacade.getOrCreateResourceStorageLoadable(this);
|
||||
load(in)
|
||||
} else {
|
||||
super.load(options)
|
||||
}
|
||||
}
|
||||
|
||||
def void load(ResourceStorageLoadable storageInputStream) {
|
||||
if (storageInputStream == null) {
|
||||
throw new NullPointerException('storageInputStream')
|
||||
}
|
||||
val task = Stopwatches.forTask("Loading from storage")
|
||||
task.start
|
||||
isLoading = true;
|
||||
isLoadedFromStorage = true;
|
||||
try {
|
||||
storageInputStream.loadIntoResource(this);
|
||||
isLoaded = true;
|
||||
} finally {
|
||||
isLoading = false
|
||||
task.stop
|
||||
}
|
||||
}
|
||||
|
||||
override protected doUnload() {
|
||||
super.doUnload
|
||||
isLoadedFromStorage = false;
|
||||
}
|
||||
|
||||
override protected clearInternalState() {
|
||||
isLoadedFromStorage = false;
|
||||
super.clearInternalState();
|
||||
}
|
||||
|
||||
override getEObject(String uriFragment) {
|
||||
if (portableURIs.isPortableURIFragment(uriFragment)) {
|
||||
return portableURIs.resolve(this, uriFragment)
|
||||
}
|
||||
super.getEObject(uriFragment)
|
||||
}
|
||||
|
||||
override protected getUnresolvableURIFragments() {
|
||||
if (isLoadedFromStorage) {
|
||||
return #{UNRESOLVABLE_FRAGMENT}
|
||||
} else {
|
||||
return super.getUnresolvableURIFragments()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionManager
|
||||
import org.eclipse.emf.ecore.resource.Resource
|
||||
import org.eclipse.xtext.resource.persistence.StorageAwareResource
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*
|
||||
* @since 2.8
|
||||
*/
|
||||
class StorageAwareResourceDescriptionManager extends DefaultResourceDescriptionManager {
|
||||
|
||||
override getResourceDescription(Resource resource) {
|
||||
switch resource {
|
||||
StorageAwareResource case resource.resourceDescription != null
|
||||
: resource.resourceDescription
|
||||
default : super.getResourceDescription(resource)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,9 @@ Generated with Xtext
|
|||
*/
|
||||
package org.eclipse.xtext.linking;
|
||||
|
||||
import org.eclipse.xtext.resource.XtextResource;
|
||||
import org.eclipse.xtext.resource.persistence.StorageAwareResource;
|
||||
|
||||
import com.google.inject.Binder;
|
||||
|
||||
/**
|
||||
|
@ -16,5 +19,9 @@ public class LangATestLanguageRuntimeModule extends AbstractLangATestLanguageRun
|
|||
|
||||
// extend configuration here
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends XtextResource> bindXtextResource() {
|
||||
return StorageAwareResource.class;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import org.eclipse.emf.ecore.EObject
|
||||
import org.eclipse.emf.ecore.EcorePackage
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.eclipse.xtext.junit4.AbstractXtextTests
|
||||
import org.eclipse.xtext.linking.LangATestLanguageStandaloneSetup
|
||||
import org.eclipse.xtext.resource.XtextResourceSet
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.xtext.linking.langATestLanguage.Main
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class PortableURIsTest extends AbstractXtextTests {
|
||||
|
||||
override setUp() throws Exception {
|
||||
super.setUp();
|
||||
with(new LangATestLanguageStandaloneSetup());
|
||||
}
|
||||
|
||||
@Test def void testPortableUris() {
|
||||
val resourceSet = get(XtextResourceSet)
|
||||
val resourceA = resourceSet.createResource(URI.createURI("hubba:/bubba.langatestlanguage")) as StorageAwareResource
|
||||
val resourceB = resourceSet.createResource(URI.createURI("hubba:/bubba2.langatestlanguage")) as StorageAwareResource
|
||||
resourceB.load(getAsStream('''
|
||||
type B
|
||||
'''), null)
|
||||
resourceA.load(getAsStream('''
|
||||
import 'hubba:/bubba2.langatestlanguage'
|
||||
|
||||
type A extends B
|
||||
'''), null)
|
||||
val extended = resourceA.contents.filter(Main).head.types.head.extends
|
||||
val uri = EcoreUtil.getURI(extended)
|
||||
val portableURI = resourceA.portableURIs.toPortableURI(resourceA, uri.trimFragment, uri.fragment)
|
||||
assertEquals(resourceA.URI, portableURI.trimFragment)
|
||||
assertTrue(resourceA.portableURIs.isPortableURIFragment(portableURI.fragment))
|
||||
assertSame(extended, resourceA.getEObject(portableURI.fragment))
|
||||
}
|
||||
|
||||
@Test def void testEObjectRelativeFragments() {
|
||||
checkFragmentBothDirections(EcorePackage.eINSTANCE, EcorePackage.eINSTANCE.EAnnotation_Details)
|
||||
checkFragmentBothDirections(EcorePackage.eINSTANCE.EAttribute_EAttributeType, EcorePackage.eINSTANCE.EAttribute_EAttributeType)
|
||||
try {
|
||||
checkFragmentBothDirections(EcorePackage.eINSTANCE.EAnnotation_EModelElement, EcorePackage.eINSTANCE.EAttribute_EAttributeType)
|
||||
Assert.fail();
|
||||
} catch (IllegalStateException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
def checkFragmentBothDirections(EObject container, EObject child) {
|
||||
val fragments = new PortableURIs()
|
||||
val fragment = fragments.getFragment(container, child)
|
||||
Assert.assertSame(child, fragments.getEObject(container, fragment))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.persistence
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import org.eclipse.emf.common.util.URI
|
||||
import org.eclipse.emf.ecore.EcorePackage
|
||||
import org.eclipse.xtext.naming.QualifiedName
|
||||
import org.eclipse.xtext.resource.persistence.SerializableEObjectDescription
|
||||
import org.eclipse.xtext.resource.persistence.SerializableReferenceDescription
|
||||
import org.eclipse.xtext.resource.persistence.SerializableResourceDescription
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* @author Sven Efftinge - Initial contribution and API
|
||||
*/
|
||||
class SerializableResourceDescriptionTest {
|
||||
|
||||
@Test def void testSerialization() {
|
||||
val uri = URI::createURI("file:/foo/bar.baz.foo")
|
||||
val before = new SerializableResourceDescription => [
|
||||
URI = uri
|
||||
references = #[
|
||||
new SerializableReferenceDescription => [
|
||||
sourceEObjectUri = uri.appendFragment('foo')
|
||||
targetEObjectUri = uri.appendFragment('hubble')
|
||||
containerEObjectURI = uri.appendFragment('baz')
|
||||
EReference = EcorePackage.eINSTANCE.EAnnotation_Contents
|
||||
indexInList = 1
|
||||
],
|
||||
new SerializableReferenceDescription => [
|
||||
sourceEObjectUri = uri.appendFragment('foo2')
|
||||
targetEObjectUri = uri.appendFragment('hubble2')
|
||||
containerEObjectURI = uri.appendFragment('baz2')
|
||||
EReference = EcorePackage.eINSTANCE.EAnnotation_Contents
|
||||
indexInList = 2
|
||||
]
|
||||
]
|
||||
descriptions = #[
|
||||
new SerializableEObjectDescription => [
|
||||
EObjectURI = uri.appendFragment('baz')
|
||||
qualifiedName = QualifiedName.create('foo','baz')
|
||||
EClass = EcorePackage.eINSTANCE.EAttribute
|
||||
userData = newHashMap('myKey' -> 'myValue')
|
||||
]
|
||||
]
|
||||
importedNames = #[QualifiedName.create('foo'), QualifiedName.create('foo','bar')]
|
||||
]
|
||||
|
||||
val bout = new ByteArrayOutputStream()
|
||||
val objectOut = new ObjectOutputStream(bout)
|
||||
objectOut.writeObject(before)
|
||||
val in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray))
|
||||
val after = in.readObject as SerializableResourceDescription
|
||||
assertEquals(before.URI, after.URI)
|
||||
assertEquals(before.importedNames, after.importedNames)
|
||||
assertEquals(before.references.size, after.references.size)
|
||||
for (int i : 0..<before.references.size) {
|
||||
val beforeRef = before.references.get(i)
|
||||
val afterRef = after.references.get(i)
|
||||
assertEquals(beforeRef.containerEObjectURI, afterRef.containerEObjectURI)
|
||||
assertEquals(beforeRef.sourceEObjectUri, afterRef.sourceEObjectUri)
|
||||
assertEquals(beforeRef.targetEObjectUri, afterRef.targetEObjectUri)
|
||||
assertEquals(beforeRef.EReference, afterRef.EReference)
|
||||
assertEquals(beforeRef.indexInList, afterRef.indexInList)
|
||||
}
|
||||
assertEquals(before.descriptions.size, after.descriptions.size)
|
||||
for (int i : 0..<before.descriptions.size) {
|
||||
val beforeDesc = before.descriptions.get(i)
|
||||
val afterDesc = after.descriptions.get(i)
|
||||
assertEquals(beforeDesc.EClass, afterDesc.EClass)
|
||||
assertEquals(beforeDesc.name, afterDesc.name)
|
||||
assertEquals(beforeDesc.qualifiedName, afterDesc.qualifiedName)
|
||||
assertEquals(beforeDesc.userData, afterDesc.userData)
|
||||
assertEquals(beforeDesc.EObjectURI, afterDesc.EObjectURI)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue