/** * Copyright (c) 2012 Cloudsmith Inc. and other contributors, as listed below. * 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: * Cloudsmith * */ package org.cloudsmith.geppetto.pp.dsl.ui.resource; import org.cloudsmith.geppetto.pp.dsl.linking.DiagnosticConsumerBasedMessageAcceptor; import org.cloudsmith.geppetto.pp.dsl.linking.IMessageAcceptor; import org.cloudsmith.geppetto.pp.dsl.linking.PPResourceLinker; import org.cloudsmith.geppetto.pp.dsl.ppdoc.DocumentationAssociator; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.diagnostics.Severity; import org.eclipse.xtext.linking.lazy.LazyLinkingResource; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.IResourceScopeCache; import org.eclipse.xtext.util.OnChangeEvictingCache; import com.google.inject.Inject; /** * A PP Resource that performs PP specific linking when the resource is loaded (the first time), and * when the parse result is updated. * Note that this is UI specific, in headless runtime, the same functionality is triggered by the @link {@link PPLinker} * . * */ public class PPResource extends LazyLinkingResource { @Inject private DocumentationAssociator documentationAssociator; @Inject PPResourceLinker resourceLinker; /** * True if the pp linking has been performed. */ protected volatile boolean fullyLinked = false; /** * Protects against code calling back to this resource while it is performing pp linking */ protected volatile boolean isLinking = false; /** * By calling this method, the next call to {@link #resolveLazyCrossReferences(CancelIndicator)} will * perform PP linking. */ protected void discardLinkedState() { if(fullyLinked && !isLinking) { fullyLinked = false; isLinking = false; getCache().clear(this); } } protected void ensureLinkedState(CancelIndicator mon) { if(isLoaded && !isLoading && !isLinking && !isUpdating && !fullyLinked) { try { isLinking = true; performPPLinking(mon); fullyLinked = true; } finally { isLinking = false; getCache().clear(this); } } } /** * {@inheritDoc} * <p> * Overridden to make sure that a resource is not initialized just to compute the root URI fragment for the parse * result. */ @Override protected String getURIFragmentRootSegment(EObject eObject) { if(unloadingContents == null) { IParseResult parseResult = getParseResult(); if(parseResult != null && eObject == parseResult.getRootASTElement()) { return "0"; } } return super.getURIFragmentRootSegment(eObject); } /** * Performs PP linking, and processes documentation * * @param mon */ protected void performPPLinking(CancelIndicator mon) { final ListBasedDiagnosticConsumer diagnosticsConsumer = new ListBasedDiagnosticConsumer(); IMessageAcceptor acceptor = new DiagnosticConsumerBasedMessageAcceptor(diagnosticsConsumer); EObject model = this.getParseResult().getRootASTElement(); documentationAssociator.validateDocumentation(model, acceptor); resourceLinker.link(model, acceptor, false); if(!isValidationDisabled()) { getErrors().addAll(diagnosticsConsumer.getResult(Severity.ERROR)); getWarnings().addAll(diagnosticsConsumer.getResult(Severity.WARNING)); } } @Override public void resolveLazyCrossReferences(CancelIndicator mon) { super.resolveLazyCrossReferences(mon); ensureLinkedState(mon); } /** * Overridden to make sure that the cache is initialized during {@link #isLoading() loading}. */ @Override protected void updateInternalState(IParseResult newParseResult) { super.updateInternalState(newParseResult); // make sure that the cache adapter is installed on this resource IResourceScopeCache cache = getCache(); if(cache instanceof OnChangeEvictingCache) { ((OnChangeEvictingCache) cache).getOrCreate(this); } } @Override protected void updateInternalState(IParseResult oldParseResult, IParseResult newParseResult) { if(fullyLinked) { discardLinkedState(); } super.updateInternalState(oldParseResult, newParseResult); EObject model = newParseResult.getRootASTElement(); documentationAssociator.linkDocumentation(model); } }