| /** |
| * Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * 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: |
| * Florian Pirchner - Initial implementation |
| */ |
| package org.eclipse.osbp.dsl.xtext.lazyresolver; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.ListIterator; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.resource.impl.ResourceImpl; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.xtext.common.types.JvmDeclaredType; |
| import org.eclipse.xtext.linking.ILinkingDiagnosticMessageProvider; |
| import org.eclipse.xtext.linking.ILinkingDiagnosticMessageProvider.ILinkingDiagnosticContext; |
| import org.eclipse.xtext.linking.impl.LinkingHelper; |
| import org.eclipse.xtext.nodemodel.INode; |
| import org.eclipse.xtext.resource.IDerivedStateComputer; |
| import org.eclipse.xtext.resource.SaveOptions; |
| import org.eclipse.xtext.util.Triple; |
| import org.eclipse.xtext.xbase.resource.BatchLinkableResource; |
| import org.eclipse.osbp.dsl.xtext.lazyresolver.LazyJvmTypeLinkingHelper.IJvmLinkCrossRefStringEnhancer; |
| import org.eclipse.osbp.dsl.xtext.lazyresolver.api.DerivedRootAdapter; |
| import org.eclipse.osbp.dsl.xtext.lazyresolver.api.IIndexDerivedStateComputer; |
| import org.eclipse.osbp.dsl.xtext.lazyresolver.api.ISemanticLoadingResource; |
| import org.eclipse.osbp.dsl.xtext.lazyresolver.api.logger.TimeLogger; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.google.inject.Inject; |
| import com.google.inject.Provider; |
| |
| @SuppressWarnings("restriction") |
| public class SemanticLoadingResource extends BatchLinkableResource implements |
| ISemanticLoadingResource { |
| |
| private static final Logger LOGGER = LoggerFactory |
| .getLogger(SemanticLoadingResource.class); |
| |
| @Inject |
| private LazyJvmTypeLinkingHelper jvmProxyLinkingHelper; |
| |
| @Inject |
| private IDerivedStateComputer derivedStateComputer; |
| |
| private boolean suppressDerivedState; |
| |
| public SemanticLoadingResource() { |
| |
| } |
| |
| @Override |
| public EObject getSemanticElement(String fragment) { |
| try { |
| suppressDerivedState = true; |
| return super.getEObject(fragment); |
| } finally { |
| suppressDerivedState = false; |
| } |
| } |
| |
| @Override |
| public EObject getSemanticElement() { |
| try { |
| suppressDerivedState = true; |
| return getContents().get(0); |
| } finally { |
| suppressDerivedState = false; |
| } |
| } |
| |
| protected void resolveLazyCrossReference(InternalEObject source, |
| EStructuralFeature crossRef) { |
| if (isPotentialLazyCrossReference(crossRef) |
| && !isJvmHelperLink(source, crossRef)) { |
| doResolveLazyCrossReference(source, crossRef); |
| } |
| } |
| |
| protected boolean isJvmHelperLink(InternalEObject source, |
| EStructuralFeature crossRef) { |
| EStructuralFeature containingFeature = source.eContainingFeature(); |
| return jvmProxyLinkingHelper.isJvmLink(containingFeature); |
| } |
| |
| public void installDerivedState(boolean preIndexingPhase) { |
| if (preIndexingPhase || !suppressDerivedState) { |
| |
| if (!isLoaded) |
| throw new IllegalStateException( |
| "The resource must be loaded, before installDerivedState can be called."); |
| if (!fullyInitialized && !isInitializing) { |
| try { |
| isInitializing = true; |
| if (derivedStateComputer != null) |
| derivedStateComputer.installDerivedState(this, |
| preIndexingPhase); |
| fullyInitialized = true; |
| } finally { |
| isInitializing = false; |
| getCache().clear(this); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void doSave(OutputStream outputStream, Map<?, ?> options) |
| throws IOException { |
| |
| if (getContents().isEmpty()) { |
| // TODO check me |
| return; |
| } |
| |
| EObject semanticElement = getSemanticElement(); |
| if (semanticElement == null) |
| throw new IllegalStateException( |
| "The Xtext resource must contain at least one element."); |
| SaveOptions saveOptions = SaveOptions.getOptions(options); |
| setEncodingFromOptions(options); |
| getSerializer().serialize(semanticElement, |
| new OutputStreamWriter(outputStream, getEncoding()), |
| saveOptions); |
| } |
| |
| @Override |
| public EObject getEObject(String uriFragment) { |
| |
| // look up the fragment cache |
| FragmentCache cache = getFragmentCache(); |
| // if (cache.containsKey(uriFragment)) { |
| // LOGGER.debug("Firstlevel-Fragment-Cache-Hit for " + uriFragment); |
| // return cache.get(uriFragment); |
| // } |
| |
| EObject result = null; |
| if (uriFragment.startsWith("/0")) { |
| // will access semantic model -> No need to install derived state |
| try { |
| suppressDerivedState = true; |
| result = super.getEObject(uriFragment); |
| } finally { |
| suppressDerivedState = false; |
| } |
| } else { |
| result = super.getEObject(uriFragment); |
| } |
| |
| // put result to fragment cache |
| cache.put(uriFragment, result); |
| |
| return result; |
| } |
| |
| protected void clearInternalState() { |
| try { |
| suppressDerivedState = true; |
| super.clearInternalState(); |
| } finally { |
| suppressDerivedState = false; |
| } |
| } |
| |
| @SuppressWarnings("sync-override") |
| @Override |
| public EList<EObject> getContents() { |
| if (derivedStateComputer instanceof IIndexDerivedStateComputer) { |
| synchronized (getLock()) { |
| if (isLoaded && !isLoading && !isInitializing && !isUpdating |
| && !fullyInitialized) { |
| try { |
| eSetDeliver(false); |
| installDerivedState(false); |
| } finally { |
| eSetDeliver(true); |
| } |
| } |
| return doGetMyContents(); |
| } |
| } else { |
| return super.getContents(); |
| } |
| } |
| |
| /** |
| * @since 2.4 |
| */ |
| protected EList<EObject> doGetMyContents() { |
| if (contents == null) { |
| contents = new ContentsList<EObject>(); |
| } |
| return contents; |
| } |
| |
| protected ILinkingDiagnosticContext createDiagnosticMessageContext( |
| Triple<EObject, EReference, INode> triple) { |
| return new JvmLinkAwareDiagnosticMessageContext(triple, |
| getLinkingHelper(), jvmProxyLinkingHelper); |
| } |
| |
| public FragmentCache getFragmentCache() { |
| return getCache().get("osbp-fragments", this, |
| new Provider<FragmentCache>() { |
| public FragmentCache get() { |
| return new FragmentCache(); |
| } |
| }); |
| } |
| |
| @SuppressWarnings("serial") |
| protected class ContentsList<E extends Object & EObject> extends |
| ResourceImpl.ContentsEList<E> { |
| |
| private boolean initializing; |
| |
| @Override |
| public E get(int index) { |
| if (!initializing && fullyInitialized && index > 0) { |
| try { |
| initializing = true; |
| EObject context = super.get(index); |
| if (isUnDerived(context)) { |
| TimeLogger logger = TimeLogger.start(getClass()); |
| IndexDerivedStateComputer computer = (IndexDerivedStateComputer) derivedStateComputer; |
| computer.installDerivedState( |
| SemanticLoadingResource.this, |
| (JvmDeclaredType) context, false); |
| logger.stop(LOGGER, "Inferring index " + index |
| + " for resource " + getURI() + " took"); |
| } |
| } finally { |
| initializing = false; |
| } |
| } else if (initializing && fullyInitialized && index > 0) { |
| if (isDerivedStateChainAllowed()) { |
| EObject context = super.get(index); |
| if (isUnDerived(context)) { |
| TimeLogger logger = TimeLogger.start(getClass()); |
| IndexDerivedStateComputer computer = (IndexDerivedStateComputer) derivedStateComputer; |
| computer.installDerivedState( |
| SemanticLoadingResource.this, |
| (JvmDeclaredType) context, false); |
| logger.stop(LOGGER, "Inferring index " + index |
| + " for resource " + getURI() + " took"); |
| } |
| } else { |
| LOGGER.error(String |
| .format("Index %s should be inferred. But another inferring is already active. Ensure you use the xyzJvm proxies in jvmInferrer. See LazyJvmTypeLinkingHelper. To allow this issue, remove -D%s in vm-arguments.", |
| Integer.toString(index), |
| SYS_PROPERTY__DERIVED_STATE_CHAIN_DISALLOWED)); |
| throw new IllegalStateException( |
| String.format( |
| "Index %s should be inferred. But another inferring is already active. Ensure you use the xyzJvm proxies in jvmInferrer. See LazyJvmTypeLinkingHelper. To allow this issue, remove -D%s in vm-arguments.", |
| Integer.toString(index), |
| SYS_PROPERTY__DERIVED_STATE_CHAIN_DISALLOWED)); |
| } |
| } |
| return super.size() > index ? super.get(index) : null; |
| } |
| |
| private boolean isDerivedStateChainAllowed() { |
| return System |
| .getProperty(SYS_PROPERTY__DERIVED_STATE_CHAIN_DISALLOWED) == null; |
| } |
| |
| private boolean isUnDerived(EObject context) { |
| return EcoreUtil.getAdapter(context.eAdapters(), |
| DerivedRootAdapter.class) != null; |
| } |
| |
| @Override |
| public Iterator<E> iterator() { |
| return super.iterator(); |
| } |
| |
| @Override |
| public ListIterator<E> listIterator() { |
| return super.listIterator(); |
| } |
| |
| @Override |
| public ListIterator<E> listIterator(int index) { |
| return super.listIterator(index); |
| } |
| } |
| |
| protected static class JvmLinkAwareDiagnosticMessageContext implements |
| ILinkingDiagnosticMessageProvider.ILinkingDiagnosticContext { |
| |
| private final Triple<EObject, EReference, INode> triple; |
| private final String linkText; |
| |
| protected JvmLinkAwareDiagnosticMessageContext( |
| Triple<EObject, EReference, INode> triple, |
| LinkingHelper linkingHelper, |
| LazyJvmTypeLinkingHelper crossRefStringHelper) { |
| this.triple = triple; |
| this.linkText = getCrossRefString(triple, linkingHelper, |
| crossRefStringHelper); |
| } |
| |
| private String getCrossRefString( |
| Triple<EObject, EReference, INode> triple, |
| LinkingHelper linkingHelper, |
| LazyJvmTypeLinkingHelper crossRefStringHelper) { |
| String result = linkingHelper.getCrossRefNodeAsString( |
| triple.getThird(), true); |
| |
| EStructuralFeature containingFeature = triple.getFirst() |
| .eContainingFeature(); |
| IJvmLinkCrossRefStringEnhancer enhancer = crossRefStringHelper |
| .getEnhancer(containingFeature); |
| if (enhancer != null) { |
| result = enhancer.enhance(triple.getFirst(), containingFeature, |
| result); |
| } |
| |
| return result; |
| } |
| |
| public EObject getContext() { |
| return triple.getFirst(); |
| } |
| |
| public EReference getReference() { |
| return triple.getSecond(); |
| } |
| |
| public String getLinkText() { |
| return linkText; |
| } |
| } |
| |
| /** |
| * A cache that caches the uriFragment with the scoped result. |
| */ |
| @SuppressWarnings("serial") |
| protected class FragmentCache extends HashMap<String, EObject> { |
| |
| public FragmentCache() { |
| } |
| |
| } |
| } |