/** * Copyright (c) 2012 committers of YAKINDU 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: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.sct.model.sgraph.resource; import java.io.StringReader; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; 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.impl.EClassImpl; import org.eclipse.emf.ecore.util.EContentAdapter; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.InternalEList; import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl; import org.eclipse.xtext.Constants; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.XtextFactory; import org.eclipse.xtext.diagnostics.DiagnosticMessage; import org.eclipse.xtext.diagnostics.Severity; import org.eclipse.xtext.linking.ILinker; import org.eclipse.xtext.linking.ILinkingDiagnosticMessageProvider; import org.eclipse.xtext.linking.ILinkingDiagnosticMessageProvider.ILinkingDiagnosticContext; import org.eclipse.xtext.linking.ILinkingService; import org.eclipse.xtext.linking.impl.LinkingHelper; import org.eclipse.xtext.linking.impl.XtextLinkingDiagnostic; import org.eclipse.xtext.linking.lazy.LazyURIEncoder; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.parser.IParser; import org.eclipse.xtext.parsetree.reconstr.XtextSerializationException; import org.eclipse.xtext.resource.XtextSyntaxDiagnostic; import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer; import org.eclipse.xtext.serializer.ISerializer; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.Triple; import org.eclipse.xtext.validation.IConcreteSyntaxValidator; import org.yakindu.sct.model.sgraph.SGraphPackage; import org.yakindu.sct.model.sgraph.SpecificationElement; import org.yakindu.sct.model.sgraph.State; import org.yakindu.sct.model.sgraph.Statechart; import org.yakindu.sct.model.sgraph.Transition; import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.inject.Inject; import com.google.inject.name.Named; /** * * @author andreas muelder - Initial contribution and API * */ public abstract class AbstractSCTResource extends XMIResourceImpl { public static final String SCT_PREFIX = "SCT_"; @Inject private IParser parser; @Inject private ILinker linker; @Inject private ISerializer serializer; @Inject private LazyURIEncoder encoder; @Inject private ILinkingService linkingService; @Inject private ILinkingDiagnosticMessageProvider diagnosticMessageProvider; @Inject private LinkingHelper linkingHelper; @Inject @Named(Constants.LANGUAGE_NAME) private String languageName; protected boolean isParsing = false; protected boolean isLinking = false; protected boolean isSerializing = false; private boolean serializerEnabled = false; protected Multimap<SpecificationElement, Diagnostic> syntaxDiagnostics; protected Multimap<SpecificationElement, Diagnostic> linkingDiagnostics; protected Map<SpecificationElement, Diagnostic> sublinkDiagnostics; protected abstract void parseTransition(Transition element); protected abstract void parseState(State element); protected abstract void parseStatechart(Statechart element); protected abstract void serializeStatechart(Statechart element); protected abstract void serializeState(State element); protected abstract void serializeTransition(Transition element); public AbstractSCTResource(URI uri) { super(uri); syntaxDiagnostics = HashMultimap.create(); linkingDiagnostics = HashMultimap.create(); setIntrinsicIDToEObjectMap(new HashMap<String, EObject>()); } @Override protected boolean useUUIDs() { return true; } @Override protected boolean useIDAttributes() { return true; } @Override protected void attachedHelper(EObject eObject) { super.attachedHelper(eObject); if (eObject instanceof SpecificationElement) { Adapter parseAdapter = EcoreUtil.getExistingAdapter(eObject, ParseAdapter.class); if (parseAdapter == null) { parseSpecificationElement((SpecificationElement) eObject); linkSpecificationElement((SpecificationElement) eObject); eObject.eAdapters().add(new ParseAdapter()); } Adapter serializeAdapter = EcoreUtil.getExistingAdapter(eObject, SerializeAdapter.class); if (serializeAdapter == null) { eObject.eAdapters().add(new SerializeAdapter()); } } } @Override public void detached(EObject eObject) { super.detached(eObject); DETACHED_EOBJECT_TO_ID_MAP.clear(); } @Override protected void detachedHelper(EObject eObject) { if (eObject instanceof SpecificationElement) { Adapter parseAdapter = EcoreUtil.getExistingAdapter(eObject, ParseAdapter.class); if (parseAdapter != null) { eObject.eAdapters().remove(parseAdapter); } Adapter serializeAdapter = EcoreUtil.getExistingAdapter(eObject, SerializeAdapter.class); if (serializeAdapter != null) { eObject.eAdapters().remove(serializeAdapter); } } super.detachedHelper(eObject); } @Override public synchronized EObject getEObject(String uriFragment) { if (encoder.isCrossLinkFragment(this, uriFragment)) { Triple<EObject, EReference, INode> triple = encoder.decode(this, uriFragment); List<EObject> linkedObjects = null; linkedObjects = linkingService.getLinkedObjects(triple.getFirst(), triple.getSecond(), triple.getThird()); if (!linkedObjects.isEmpty()) { return linkedObjects.get(0); } else { createDiagnostic(triple); return null; } } if (uriFragment != null && uriFragment.startsWith(SCT_PREFIX)) { return super.getEObject(uriFragment.substring(SCT_PREFIX.length())); } return super.getEObject(uriFragment); } @Override public synchronized String getURIFragment(EObject eObject) { if (unloadingContents != null) { return super.getURIFragment(eObject); } ICompositeNode node = NodeModelUtils.findActualNodeFor(eObject); if (node != null) { String fragment = super.getURIFragment(eObject); if (!Strings.isNullOrEmpty(fragment)) { return SCT_PREFIX + fragment; } } return super.getURIFragment(eObject); } @Override public String getID(EObject eObject) { if (NodeModelUtils.getNode(eObject) != null) return null; return super.getID(eObject); } protected void createDiagnostic(Triple<EObject, EReference, INode> triple) { SpecificationElement specificationElement = EcoreUtil2.getContainerOfType(triple.getFirst(), SpecificationElement.class); DiagnosticMessage message = createDiagnosticMessage(triple); Diagnostic diagnostic = new XtextLinkingDiagnostic(triple.getThird(), message.getMessage(), message.getIssueCode(), message.getIssueData()); linkingDiagnostics.put(specificationElement, diagnostic); } protected DiagnosticMessage createDiagnosticMessage(Triple<EObject, EReference, INode> triple) { ILinkingDiagnosticMessageProvider.ILinkingDiagnosticContext context = createDiagnosticMessageContext(triple); DiagnosticMessage message = diagnosticMessageProvider.getUnresolvedProxyMessage(context); return message; } protected ILinkingDiagnosticContext createDiagnosticMessageContext(Triple<EObject, EReference, INode> triple) { return new DiagnosticMessageContext(triple, linkingHelper); } // copied from xtext LazyLinkingResource public void resolveLazyCrossReferences(CancelIndicator monitor) { TreeIterator<Object> iterator = EcoreUtil.getAllContents(this, true); while (iterator.hasNext()) { InternalEObject source = (InternalEObject) iterator.next(); EStructuralFeature[] eStructuralFeatures = ((EClassImpl.FeatureSubsetSupplier) source.eClass() .getEAllStructuralFeatures()).crossReferences(); if (eStructuralFeatures != null) { for (EStructuralFeature crossRef : eStructuralFeatures) { if (monitor.isCanceled()) { return; } resolveLazyCrossReference(source, crossRef, monitor); } } } } // copied from xtext LazyLinkingResource protected void resolveLazyCrossReference(InternalEObject source, EStructuralFeature crossRef, CancelIndicator monitor) { if (crossRef.isDerived()) return; if (crossRef.isMany()) { @SuppressWarnings("unchecked") InternalEList<EObject> list = (InternalEList<EObject>) source.eGet(crossRef); for (int i = 0; i < list.size(); i++) { EObject proxy = list.basicGet(i); if (proxy.eIsProxy()) { URI proxyURI = ((InternalEObject) proxy).eProxyURI(); if (getURI().equals(proxyURI.trimFragment())) { final String fragment = proxyURI.fragment(); if (encoder.isCrossLinkFragment(this, fragment) && !monitor.isCanceled()) { EObject target = getEObject(fragment); if (target != null) { try { source.eSetDeliver(false); list.setUnique(i, target); } finally { source.eSetDeliver(true); } } } } } } } else { EObject proxy = (EObject) source.eGet(crossRef, false); if (proxy != null && proxy.eIsProxy()) { URI proxyURI = ((InternalEObject) proxy).eProxyURI(); if (getURI().equals(proxyURI.trimFragment())) { final String fragment = proxyURI.fragment(); if (encoder.isCrossLinkFragment(this, fragment) && !monitor.isCanceled()) { EObject target = getEObject(fragment); if (target != null) { try { source.eSetDeliver(false); source.eSet(crossRef, target); } finally { source.eSetDeliver(true); } } } } } } } public void parseSpecificationElement(SpecificationElement element) { Assert.isNotNull(element); isParsing = true; if (element instanceof Transition) { parseTransition((Transition) element); } else if (element instanceof State) { parseState((State) element); } else if (element instanceof Statechart) { parseStatechart((Statechart) element); } isParsing = false; } protected IParseResult parse(SpecificationElement element, String rule) { ParserRule parserRule = XtextFactory.eINSTANCE.createParserRule(); parserRule.setName(rule); String specification = element.getSpecification(); IParseResult result = parser.parse(parserRule, new StringReader(specification != null ? specification : "")); createDiagnostics(result, element); return result; } protected void createDiagnostics(IParseResult parseResult, SpecificationElement semanticTarget) { syntaxDiagnostics.get(semanticTarget).clear(); for (INode error : parseResult.getSyntaxErrors()) { syntaxDiagnostics.put(semanticTarget, new XtextSyntaxDiagnostic(error)); } } public void linkSpecificationElements() { TreeIterator<EObject> iter = getAllContents(); while (iter.hasNext()) { EObject currentObject = iter.next(); if (currentObject instanceof SpecificationElement) { linkSpecificationElement((SpecificationElement) currentObject); } } } protected void linkSpecificationElement(SpecificationElement element) { isLinking = true; final ListBasedDiagnosticConsumer consumer = new ListBasedDiagnosticConsumer(); linker.linkModel(element, consumer); linkingDiagnostics.get(element).clear(); linkingDiagnostics.putAll(element, (consumer.getResult(Severity.ERROR))); linkingDiagnostics.putAll(element, (consumer.getResult(Severity.WARNING))); isLinking = false; } protected void serializeSpecificationElement(SpecificationElement element) { if (getSyntaxDiagnostics().get(element).size() > 0 || getLinkingDiagnostics().get(element).size() > 0) { return; } try { isSerializing = true; if (element instanceof Transition) { serializeTransition((Transition) element); } else if (element instanceof State) { serializeState((State) element); } else if (element instanceof Statechart) { serializeStatechart((Statechart) element); } } catch (XtextSerializationException ex) { // Leave the old specification } catch (IConcreteSyntaxValidator.InvalidConcreteSyntaxException ex) { // Leave the old specification } finally { isSerializing = false; } } protected String serialize(EObject object) { if (object != null) { return serializer.serialize(object); } return ""; } public Multimap<SpecificationElement, Diagnostic> getSyntaxDiagnostics() { return syntaxDiagnostics; } public Multimap<SpecificationElement, Diagnostic> getLinkingDiagnostics() { return linkingDiagnostics; } public String getLanguageName() { return languageName; } protected final class SerializeAdapter extends EContentAdapter { @Override public void notifyChanged(Notification msg) { super.notifyChanged(msg); if (isSerializerEnabled()) { if (isLoading() || isParsing || isLinking || isSerializing) { return; } if (msg.getEventType() == Notification.REMOVING_ADAPTER || msg.getEventType() == Notification.RESOLVE) { return; } Object notifier = msg.getNotifier(); if (notifier instanceof EObject) { EObject eObject = (EObject) notifier; SpecificationElement container = EcoreUtil2.getContainerOfType(eObject, SpecificationElement.class); if (container != null) { serializeSpecificationElement(container); } } } } @Override public boolean isAdapterForType(Object type) { return SerializeAdapter.class == type; } } protected final class ParseAdapter extends AdapterImpl { @Override public void notifyChanged(Notification msg) { if (isSerializing) { return; } if (msg.getFeature() == SGraphPackage.Literals.SPECIFICATION_ELEMENT__SPECIFICATION) { String newValString = msg.getNewStringValue(); String oldVString = msg.getOldStringValue(); if (newValString != null && !newValString.equals(oldVString)) { parseSpecificationElement((SpecificationElement) msg.getNotifier()); linkSpecificationElements(); } } } @Override public boolean isAdapterForType(Object type) { return ParseAdapter.class == type; } } public boolean isSerializerEnabled() { return serializerEnabled; } public void setSerializerEnabled(boolean serializerEnabled) { this.serializerEnabled = serializerEnabled; } // copied from xtext LazyLinkingResource protected static class DiagnosticMessageContext implements ILinkingDiagnosticMessageProvider.ILinkingDiagnosticContext { private final Triple<EObject, EReference, INode> triple; private final LinkingHelper linkingHelper; protected DiagnosticMessageContext(Triple<EObject, EReference, INode> triple, LinkingHelper helper) { this.triple = triple; this.linkingHelper = helper; } public EObject getContext() { return triple.getFirst(); } public EReference getReference() { return triple.getSecond(); } public String getLinkText() { return linkingHelper.getCrossRefNodeAsString(triple.getThird(), true); } } /** * overridden because original calls 'Util.denormalizeURI(uri, * getResourceSet())' which leads into error if there is no platform * available */ @Override public void setURI(URI uri) { if (getResourceSet() != null) { setRawURI(uri); } } public void setRawURI(URI uri) { URI oldURI = getURI(); if ((uri == oldURI) || ((uri != null) && (uri.equals(oldURI)))) return; super.setURI(uri); } }