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

org.eclipse.xtext.resource.XtextResource Maven / Gradle / Ivy

There is a newer version: 2.4.3
Show newest version
/*******************************************************************************
 * Copyright (c) 2008 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 java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.resource.impl.ResourceImpl;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.linking.ILinker;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
import org.eclipse.xtext.parser.IEncodingProvider;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.parser.IParser;
import org.eclipse.xtext.parser.antlr.IReferableElementsUnloader;
import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.util.IResourceScopeCache;
import org.eclipse.xtext.util.ReplaceRegion;
import org.eclipse.xtext.util.StringInputStream;
import org.eclipse.xtext.util.TextRegion;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.validation.IConcreteSyntaxValidator;
import org.eclipse.xtext.validation.IConcreteSyntaxValidator.IDiagnosticAcceptor;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;

/**
 * An EMF resource that reads and writes models of an Xtext DSL.
 * 
 * @author Jan K?hnlein
 * @author Heiko Behrens
 * @author Dennis H?bner
 * @author Moritz Eysholdt
 * @author Sebastian Zarnekow
 * @author Sven Efftinge
 */
public class XtextResource extends ResourceImpl {

	public static final String OPTION_RESOLVE_ALL = XtextResource.class.getName() + ".RESOLVE_ALL";

	/**
	 * @deprecated use {@link SaveOptions#addTo(Map)} instead.
	 */
	@Deprecated
	public static final String OPTION_FORMAT = XtextResource.class.getName() + ".FORMAT";

	/**
	 * @deprecated use {@link SaveOptions#addTo(Map)} instead.
	 */
	@Deprecated
	public static final String OPTION_SERIALIZATION_OPTIONS = XtextResource.class.getName() + ".SERIALIZATION_OPTIONS";

	public static final String OPTION_ENCODING = XtextResource.class.getName() + ".DEFAULT_ENCODING";

	private boolean validationDisabled;
	
	/**
	 * @since 2.1
	 */
	protected volatile boolean isUpdating = false;

	private IParser parser;

	@Inject
	private ILinker linker;

	@Inject
	private IFragmentProvider fragmentProvider;
	
	@Inject
	@Named(Constants.LANGUAGE_NAME) 
	private String languageName;
	
	private IFragmentProvider.Fallback fragmentProviderFallback = new IFragmentProvider.Fallback() {
		
		public String getFragment(EObject obj) {
			return XtextResource.super.getURIFragment(obj);
		}
		
		public EObject getEObject(String fragment) {
			return XtextResource.super.getEObject(fragment);
		}
	};

	@Inject
	private ISerializer serializer;

	@Inject
	private IReferableElementsUnloader unloader;

	@Inject
	private IResourceServiceProvider resourceServiceProvider;

	@Inject
	private IConcreteSyntaxValidator validator;

	@Inject
	private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE;

	@Inject
	private IEncodingProvider encodingProvider;

	private String encoding;

	public IResourceServiceProvider getResourceServiceProvider() {
		return resourceServiceProvider;
	}

	public void setResourceServiceProvider(IResourceServiceProvider resourceServiceProvider) {
		this.resourceServiceProvider = resourceServiceProvider;
	}

	private IParseResult parseResult;

	@Inject
	protected void setInjectedParser(IParser parser) {
		this.parser = parser;
	}

	public XtextResource(URI uri) {
		super(uri);
	}

	public XtextResource() {
		super();
	}

	@Nullable
	public IParseResult getParseResult() {
		return parseResult;
	}

	@Override
	protected void doLoad(InputStream inputStream, Map options) throws IOException {
		setEncodingFromOptions(options);
		IParseResult result = parser.parse(new InputStreamReader(inputStream, getEncoding()));
		updateInternalState(this.parseResult, result);
	}

	protected void setEncodingFromOptions(Map options) {
		if (options != null) {
			Object encodingOption = options.get(OPTION_ENCODING);
			if (encodingOption instanceof String) {
				encoding = (String) encodingOption;
			}
		}
	}

	public String getEncoding() {
		if (encoding == null) {
			encoding = encodingProvider.getEncoding(getURI());
		}
		return encoding;
	}

	public void reparse(String newContent) throws IOException {
		try {
			isUpdating = true;
			clearInternalState();
			doLoad(new StringInputStream(newContent, getEncoding()), null);
			setModified(false);
		} finally {
			isUpdating = false;
		}
	}

	protected void reattachModificationTracker(EObject element) {
		if (isTrackingModification() && element != null) {
			if (!element.eAdapters().contains(modificationTrackingAdapter))
				element.eAdapters().add(modificationTrackingAdapter);
			// copied from ResourceImpl.setTrackingModification
			for (TreeIterator i = getAllProperContents(element); i.hasNext();) {
				EObject eObject = i.next();
				if (!eObject.eAdapters().contains(modificationTrackingAdapter))
					eObject.eAdapters().add(modificationTrackingAdapter);
			}
		}
	}

	@Override
	protected void doUnload() {
		super.doUnload();
		parseResult = null;
	}

	public void update(int offset, int replacedTextLength, String newText) {
		if (!isLoaded()) {
			throw new IllegalStateException("You can't update an unloaded resource.");
		}
		try {
			isUpdating = true;
			IParseResult oldParseResult = parseResult;
			ReplaceRegion replaceRegion = new ReplaceRegion(new TextRegion(offset, replacedTextLength), newText);
			IParseResult newParseResult = parser.reparse(oldParseResult, replaceRegion);
			updateInternalState(oldParseResult, newParseResult);
		} finally {
			isUpdating = false;
		}
	}

	/**
	 * @param oldParseResult the previous parse result that should be detached if necessary.
	 * @param newParseResult the current parse result that should be attached to the content of this resource
	 * @since 2.1
	 */
	protected void updateInternalState(IParseResult oldParseResult, IParseResult newParseResult) {
		if (oldParseResult != null && oldParseResult.getRootASTElement() != null && oldParseResult.getRootASTElement() != newParseResult.getRootASTElement()) {
			EObject oldRootAstElement = oldParseResult.getRootASTElement();
			if (oldRootAstElement != newParseResult.getRootASTElement()) {
				unload(oldRootAstElement);
				getContents().remove(oldRootAstElement);
			}
		}
		updateInternalState(newParseResult);
	}
	
	protected void updateInternalState(IParseResult newParseResult) {
		this.parseResult = newParseResult;
		if (parseResult.getRootASTElement() != null && !getContents().contains(parseResult.getRootASTElement()))
			getContents().add(0, parseResult.getRootASTElement());
		reattachModificationTracker(parseResult.getRootASTElement());
		clearErrorsAndWarnings();
		addSyntaxErrors();
		doLinking();
	}
	
	protected void clearErrorsAndWarnings() {
		getWarnings().clear();
		getErrors().clear();
	}

	protected void addSyntaxErrors() {
		getErrors().addAll(createDiagnostics(parseResult));
	}

	protected void unload(EObject oldRootObject) {
		if (unloader != null) {
			unloader.unloadRoot(oldRootObject);
		}
	}

	protected void clearInternalState() {
		for (EObject content : getContents()) {
			unload(content);
		}
		getContents().clear();
		clearErrorsAndWarnings();
		this.parseResult = null;
	}

	protected void doLinking() {
		if (parseResult == null || parseResult.getRootASTElement() == null)
			return;

		final ListBasedDiagnosticConsumer consumer = new ListBasedDiagnosticConsumer();
		linker.linkModel(parseResult.getRootASTElement(), consumer);
		if (!validationDisabled) {
			getErrors().addAll(consumer.getResult(Severity.ERROR));
			getWarnings().addAll(consumer.getResult(Severity.WARNING));
		}
	}

	@Override
	public EObject getEObject(String uriFragment) {
		return basicGetEObject(uriFragment);
	}

	/**
	 * Resolves a fragment to an {@link EObject}. The returned object is not necessarily
	 * contained in this resource. It may resolve to a different one, instead.
	 * The result may be null.
	 * 
	 * @see ResourceImpl#getEObject(String)
	 * @see IFragmentProvider
	 * @since 2.4
	 */
	protected EObject basicGetEObject(@NonNull String uriFragment) {
		if (fragmentProvider != null) {
			EObject result = fragmentProvider.getEObject(this, uriFragment, fragmentProviderFallback);
			return result;
		}
		EObject result = super.getEObject(uriFragment);
		return result;
	}

	@Override
	public String getURIFragment(final EObject object) {
		return cache.get(Tuples.pair(object, "fragment"), this, new Provider() {
			public String get() {
				if (fragmentProvider != null) {
					String result = fragmentProvider.getFragment(object, fragmentProviderFallback);
					return result;
				}
				String result = XtextResource.super.getURIFragment(object);
				return result;
			}
		});
	}

	@Override
	public void doSave(OutputStream outputStream, Map options) throws IOException {
		if (getContents().isEmpty())
			throw new IllegalStateException("The Xtext resource must contain at least one element.");
		SaveOptions saveOptions = SaveOptions.getOptions(options);
		setEncodingFromOptions(options);
		serializer.serialize(getContents().get(0), new OutputStreamWriter(outputStream, getEncoding()), saveOptions);
	}

	/**
	 * Creates {@link org.eclipse.emf.ecore.resource.Resource.Diagnostic diagnostics} from {@link SyntaxErrorMessage syntax errors} in {@link IParseResult}.
	 * No diagnostics will be created if {@link #isValidationDisabled() validation is disabled} for this
	 * resource.
	 * 
	 * @param parseResult the parse result that provides the syntax errors.
	 * @return list of {@link org.eclipse.emf.ecore.resource.Resource.Diagnostic}. Never null.
	 */
	private List createDiagnostics(IParseResult parseResult) {
		if (validationDisabled)
			return Collections.emptyList();

		List diagnostics = new ArrayList();
		for (INode error : parseResult.getSyntaxErrors()) {
			diagnostics.add(new XtextSyntaxDiagnostic(error));
		}
		return diagnostics;
	}

	public IParser getParser() {
		return parser;
	}

	public void setParser(IParser parser) {
		this.parser = parser;
	}

	public IConcreteSyntaxValidator getConcreteSyntaxValidator() {
		return validator;
	}

	public List validateConcreteSyntax() {
		List diagnostics = new ArrayList();
		IDiagnosticAcceptor acceptor = new IConcreteSyntaxValidator.DiagnosticListAcceptor(diagnostics);
		for (EObject obj : getContents())
			validator.validateRecursive(obj, acceptor, new HashMap());
		return diagnostics;
	}

	public ILinker getLinker() {
		return linker;
	}

	public void setLinker(ILinker linker) {
		this.linker = linker;
	}

	public IFragmentProvider getFragmentProvider() {
		return fragmentProvider;
	}

	public void setFragmentProvider(IFragmentProvider fragmentProvider) {
		this.fragmentProvider = fragmentProvider;
	}

	public ISerializer getSerializer() {
		return serializer;
	}

	public void setSerializer(ISerializer serializer) {
		this.serializer = serializer;
	}

	public void setParseResult(IParseResult parseResult) {
		this.parseResult = parseResult;
	}

	public boolean isValidationDisabled() {
		return validationDisabled;
	}

	public void setValidationDisabled(boolean validationDisabled) {
		this.validationDisabled = validationDisabled;
		if (validationDisabled) {
			clearErrorsAndWarnings();
		}
	}

	public void setUnloader(IReferableElementsUnloader unloader) {
		this.unloader = unloader;
	}

	public IReferableElementsUnloader getUnloader() {
		return unloader;
	}
	
	public IResourceScopeCache getCache() {
		return cache;
	}
	
	public void setCache(IResourceScopeCache cache) {
		this.cache = cache;
	}
	
	public String getLanguageName() {
		return languageName;
	}
	
	/**
	 * @since 2.3
	 */
	public void setLanguageName(String languageName) {
		this.languageName = languageName;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy