All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.xbase.resource.BatchLinkableResource Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2011 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.xbase.resource;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.impl.JvmDeclaredTypeImplCustom;
import org.eclipse.xtext.common.types.xtext.JvmMemberInitializableResource;
import org.eclipse.xtext.diagnostics.DiagnosticMessage;
import org.eclipse.xtext.diagnostics.ExceptionDiagnostic;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.linking.lazy.LazyURIEncoder;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.resource.CompilerPhases;
import org.eclipse.xtext.resource.DerivedStateAwareResource;
import org.eclipse.xtext.resource.IBatchLinkableResource;
import org.eclipse.xtext.resource.ISynchronizable;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;

import com.google.inject.Inject;

/**
 * A specialized EMF resource that is capable of resolving proxies in batch mode.
 * That is, on {@link #getEObject(String)}, the {@link BatchLinkingService} is used
 * to resolve a chunk of proxies. 
 * 
 * @author Sebastian Zarnekow - Linking assumptions
 */
public class BatchLinkableResource extends DerivedStateAwareResource implements ISynchronizable, IBatchLinkableResource, JvmMemberInitializableResource {
	
	private static final Logger log = Logger.getLogger(BatchLinkableResource.class);
	
	@Inject
	private BatchLinkingService batchLinkingService;
	
	@Inject
	private CompilerPhases compilerPhases;
	
	private Set jvmMemberInitializers = null;
	
	private boolean hasJvmMemberInitializers = false;
	
	private boolean isInitializingJvmMembers = false;
	
	/**
	 * Returns the lock of the owning {@link ResourceSet}, if it exposes such a lock.
	 * Otherwise this resource itself is used as the lock context.
	 */
	/* @NonNull */
	@Override
	public Object getLock() {
		ResourceSet resourceSet = getResourceSet();
		if (resourceSet instanceof ISynchronizable) {
			return ((ISynchronizable) resourceSet).getLock();
		}
		return this;
	}
	
	/**
	 * {@inheritDoc}
	 * 
	 * @since 2.4
	 */
	/* @Nullable */
	@Override
	public  Result execute(/* @NonNull */ IUnitOfWork unit) throws Exception {
		synchronized (getLock()) {
			return unit.exec(this);
		}
	}
	
	/**
	 * {@inheritDoc}
	 * 
	 * Delegates to the {@link BatchLinkingService} if the requested reference is 
	 * {@link BatchLinkingService#isBatchLinkable(EReference) linkeable in batch mode}.
	 * 
	 * Implementation detail: This specialization of {@link #getEObject(String) getEObject}
	 * synchronizes on the {@link #getLock() lock} which is exposed by the synchronizable
	 * resource rather than on the resource directly. This guards against reentrant resolution
	 * from different threads that could block each other.
	 * 
	 * Usually one would want to lock only in the {@link BatchLinkingService} but we could
	 * have intermixed {@link LazyURIEncoder#isCrossLinkFragment(org.eclipse.emf.ecore.resource.Resource, String)
	 * lazy cross reference} and vanilla EMF cross references which again could lead to a
	 * dead lock.
	 */
	@Override
	public EObject getEObject(String uriFragment) {
		synchronized (getLock()) {
			try {
				if (getEncoder().isCrossLinkFragment(this, uriFragment) && !isLoadedFromStorage()) {
					if (!getUnresolvableURIFragments().contains(uriFragment)) {
						Triple triple = getEncoder().decode(this, uriFragment);
						if (batchLinkingService.isBatchLinkable(triple.getSecond())) {
							if (compilerPhases.isIndexing(this))
								log.error("Don't resolve expressions during indexing!", new IllegalStateException("Resource URI : "+getURI()+", fragment : "+uriFragment));
							return batchLinkingService.resolveBatched(triple.getFirst(), triple.getSecond(), uriFragment);
						}
						return super.getEObject(uriFragment, triple);
					} else {
						return null;
					}
				}
				return super.getEObject(uriFragment);
			} catch (RuntimeException e) {
				operationCanceledManager.propagateAsErrorIfCancelException(e);
				getErrors().add(new ExceptionDiagnostic(e));
				log.error("resolution of uriFragment '" + uriFragment + "' failed.", e);
				// wrapped because the javaDoc of this method states that WrappedExceptions are thrown
				// logged because EcoreUtil.resolve will ignore any exceptions.
				throw new WrappedException(e);
			}
		}
	}
	
	/**
	 * {@inheritDoc}
	 * 
	 * 

Implementation detail: Overridden to use the shared {@link #getLock() lock}.

*/ @SuppressWarnings("sync-override") @Override public EList getContents() { synchronized (getLock()) { if (isLoaded && !isLoading && !isInitializing && !isUpdating && !fullyInitialized && !isLoadedFromStorage()) { try { eSetDeliver(false); installDerivedState(false); } finally { eSetDeliver(true); } } return doGetContents(); } } /** * Delegates to the BatchLinkingService to resolve all references. The linking service * is responsible to lock the resource or resource set. */ @Override public void resolveLazyCrossReferences(CancelIndicator monitor) { IParseResult parseResult = getParseResult(); if (parseResult != null) { batchLinkingService.resolveBatched(parseResult.getRootASTElement(), monitor); } operationCanceledManager.checkCanceled(monitor); super.resolveLazyCrossReferences(monitor); } @Override protected EObject handleCyclicResolution(Triple triple) throws AssertionError { if (!isValidationDisabled()) { EObject context = triple.getFirst(); if (context.eClass() == TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE) { // here we may end up with cyclic resolution requests in rare situations, e.g. for input types // like : /* * package p; * class C extends p.C.Bogus {} */ return null; } DiagnosticMessage message = new DiagnosticMessage("Cyclic linking detected : " + getReferences(triple, resolving), Severity.ERROR, "cyclic-resolution"); List list = getDiagnosticList(message); Diagnostic diagnostic = createDiagnostic(triple, message); if (!list.contains(diagnostic)) list.add(diagnostic); } return null; } @Override public void linkBatched(CancelIndicator monitor) { resolveLazyCrossReferences(monitor); } // Lazy initialization /** * Executes any {@link Runnable}s added through {@link #addJvmMemberInitializer(Runnable)} * * @since 2.8 * @noreference This method is not intended to be referenced by clients. */ @Override public void ensureJvmMembersInitialized() { if (jvmMemberInitializers == null) return; Set localRunnables = null; synchronized(this) { localRunnables = jvmMemberInitializers; jvmMemberInitializers = null; hasJvmMemberInitializers = false; } if (localRunnables == null) return; boolean wasDeliver = eDeliver(); LinkedHashSet> before = resolving; try { eSetDeliver(false); if (!before.isEmpty()) { resolving = new LinkedHashSet>(); } if (isInitializingJvmMembers) { throw new IllegalStateException("Reentrant attempt to initialize JvmMembers"); } try { isInitializingJvmMembers = true; for (Runnable runnable : localRunnables) { runnable.run(); } } finally { isInitializingJvmMembers = false; } } catch (Exception e) { log.error(e.getMessage(), e); } finally { if (!before.isEmpty()) { resolving = before; } eSetDeliver(wasDeliver); } } @Override public boolean isInitializingJvmMembers() { return isInitializingJvmMembers; } @Override protected void doDiscardDerivedState() { this.jvmMemberInitializers = null; this.hasJvmMemberInitializers = false; super.doDiscardDerivedState(); } /** * Register runnables to be executed when the {@link JvmMember members} of {@link JvmType types} * in this resource are initialized. * * @since 2.8 * @noreference This method is not intended to be referenced by clients. */ @Override public void addJvmMemberInitializer(Runnable runnable) { if (isInitializingJvmMembers) { throw new IllegalStateException("Cannot enqueue runnables during JvmMemberInitialization"); } if (this.jvmMemberInitializers == null) { this.jvmMemberInitializers = new LinkedHashSet(); this.hasJvmMemberInitializers = true; for (EObject obj : getContents()) { if (obj instanceof JvmDeclaredTypeImplCustom) { JvmDeclaredTypeImplCustom type = (JvmDeclaredTypeImplCustom) obj; markPendingInitialization(type); } } } this.jvmMemberInitializers.add(runnable); } /** * Recursively traverse the types in this resource to mark them as lazy initialized types. */ private void markPendingInitialization(JvmDeclaredTypeImplCustom type) { type.setPendingInitialization(true); for (JvmMember member : type.basicGetMembers()) { if (member instanceof JvmDeclaredTypeImplCustom) { markPendingInitialization((JvmDeclaredTypeImplCustom) member); } } } @Override public boolean hasJvmMemberInitializers() { return hasJvmMemberInitializers; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy