<commit_before>/*******************************************************************************
 * 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.linking.lazy;

import java.util.Collection;
import java.util.Iterator;
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.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
import org.eclipse.xtext.linking.impl.AbstractCleaningLinker;
import org.eclipse.xtext.linking.impl.LinkingDiagnosticProducer;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.EcoreGenericsUtil;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import org.eclipse.xtext.util.SimpleCache;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.eclipse.xtext.util.internal.Stopwatches;
import org.eclipse.xtext.util.internal.Stopwatches.StoppedTask;

import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * @author <NAME> - Initial contribution and API
 * @author <NAME>
 */
public class LazyLinker extends AbstractCleaningLinker {

	private static final Logger log = Logger.getLogger(LazyLinker.class);
	
	private SimpleCache<EClass, EClass> instantiableSubTypes = new SimpleCache<EClass, EClass>(
			new Function<EClass, EClass>() {
				public EClass apply(EClass from) {
					return findInstantiableCompatible(from);
				}
			});

	@Inject
	private LazyURIEncoder encoder;

	@Inject
	private Registry registry;

	@Inject
	private EcoreGenericsUtil ecoreGenericsUtil;
	
	@Inject
	private IGrammarAccess grammarAccess;
	
	@Inject
	private OnChangeEvictingCache cache;

	private final StoppedTask installProxiesTask = Stopwatches.forTask("LazyLinker.installProxies");

	@Override
	protected void doLinkModel(final EObject model, IDiagnosticConsumer consumer) {
		final Multimap<EStructuralFeature.Setting, INode> settingsToLink = ArrayListMultimap.create();
		final LinkingDiagnosticProducer producer = new LinkingDiagnosticProducer(consumer);
		cache.execWithoutCacheClear(model.eResource(), new IUnitOfWork.Void<Resource>() {
			@Override
			public void process(Resource state) throws Exception {
				installProxies(model, producer, settingsToLink);
				TreeIterator<EObject> iterator = model.eAllContents();
				while (iterator.hasNext()) {
					EObject eObject = iterator.next();
					installProxies(eObject, producer, settingsToLink);
				}
			}
		});
		installQueuedLinks(settingsToLink);
	}

	protected void installProxies(EObject obj, IDiagnosticProducer producer,
			Multimap<EStructuralFeature.Setting, INode> settingsToLink) {
		installProxiesTask.start();
		ICompositeNode node = NodeModelUtils.getNode(obj);
		if (node == null)
			return;
		installProxies(obj, producer, settingsToLink, node);
		installProxiesTask.stop();
	}

	private void installProxies(EObject obj, IDiagnosticProducer producer,
			Multimap<EStructuralFeature.Setting, INode> settingsToLink, ICompositeNode parentNode) {
		EClass eClass = obj.eClass();
		int crossReferenceCount = eClass.getEAllReferences().size() - eClass.getEAllContainments().size();
		if (crossReferenceCount == 0)
			return;

		Set<Object> linkedCrossReferences = Sets.newHashSetWithExpectedSize(crossReferenceCount);
		for (Iterator<INode> iterator = parentNode.getChildren().iterator(); iterator.hasNext(); ) {
			INode node = iterator.next();
			if (node.getGrammarElement() instanceof CrossReference && !Iterables.isEmpty(node.getLeafNodes())) {
				CrossReference ref = (CrossReference) node.getGrammarElement();
				producer.setNode(node);
				final EReference eRef = GrammarUtil.getReference(ref, eClass);
				if (eRef == null) {
					throw new IllegalStateException("Couldn't find EReference for crossreference " + ref);
				}
				if (!eRef.isResolveProxies() /*|| eRef.getEOpposite() != null see https://bugs.eclipse.org/bugs/show_bug.cgi?id=282486*/) {
					final EStructuralFeature.Setting setting = ((InternalEObject) obj).eSetting(eRef);
					settingsToLink.put(new SettingDelegate(setting), node);
				} else {
					createAndSetProxy(obj, node, eRef);
				}
				linkedCrossReferences.add(eRef);
			}
		}
		if (linkedCrossReferences.size() < crossReferenceCount && shouldCheckParentNode(parentNode)) {
			installProxies(obj, producer, settingsToLink, parentNode.getParent());
		}
	}

	@SuppressWarnings("unchecked")
	protected void installQueuedLinks(Multimap<EStructuralFeature.Setting, INode> settingsToLink) {
		for (EStructuralFeature.Setting setting : settingsToLink.keySet()) {
			final EObject eObject = setting.getEObject();
			final EReference eRef = (EReference) setting.getEStructuralFeature();
			final Collection<INode> nodes = settingsToLink.get(setting);
			if (setting.getEStructuralFeature().isMany()) {
				EList<EObject> list = (EList<EObject>) setting.get(false);
				for (INode node : nodes) {
					final EObject proxy = createProxy(eObject, node, eRef);
					list.add(EcoreUtil.resolve(proxy, eObject));
				}
			} else {
				final INode node = nodes.iterator().next();
				final EObject proxy = createProxy(eObject, node, eRef);
				setting.set(EcoreUtil.resolve(proxy, eObject));
			}
		}
	}

	@SuppressWarnings("unchecked")<commit_msg>Fix: Partially reverted change in LazyLinker#installProxies()<commit_after>/*******************************************************************************
 * 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.linking.lazy;

import java.util.Collection;
import java.util.Iterator;
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.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
import org.eclipse.xtext.linking.impl.AbstractCleaningLinker;
import org.eclipse.xtext.linking.impl.LinkingDiagnosticProducer;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.EcoreGenericsUtil;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import org.eclipse.xtext.util.SimpleCache;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.eclipse.xtext.util.internal.Stopwatches;
import org.eclipse.xtext.util.internal.Stopwatches.StoppedTask;

import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * @author <NAME> - Initial contribution and API
 * @author <NAME>
 */
public class LazyLinker extends AbstractCleaningLinker {

	private static final Logger log = Logger.getLogger(LazyLinker.class);
	
	private SimpleCache<EClass, EClass> instantiableSubTypes = new SimpleCache<EClass, EClass>(
			new Function<EClass, EClass>() {
				public EClass apply(EClass from) {
					return findInstantiableCompatible(from);
				}
			});

	@Inject
	private LazyURIEncoder encoder;

	@Inject
	private Registry registry;

	@Inject
	private EcoreGenericsUtil ecoreGenericsUtil;
	
	@Inject
	private IGrammarAccess grammarAccess;
	
	@Inject
	private OnChangeEvictingCache cache;

	private final StoppedTask installProxiesTask = Stopwatches.forTask("LazyLinker.installProxies");

	@Override
	protected void doLinkModel(final EObject model, IDiagnosticConsumer consumer) {
		final Multimap<EStructuralFeature.Setting, INode> settingsToLink = ArrayListMultimap.create();
		final LinkingDiagnosticProducer producer = new LinkingDiagnosticProducer(consumer);
		cache.execWithoutCacheClear(model.eResource(), new IUnitOfWork.Void<Resource>() {
			@Override
			public void process(Resource state) throws Exception {
				installProxies(model, producer, settingsToLink);
				TreeIterator<EObject> iterator = model.eAllContents();
				while (iterator.hasNext()) {
					EObject eObject = iterator.next();
					installProxies(eObject, producer, settingsToLink);
				}
			}
		});
		installQueuedLinks(settingsToLink);
	}

	protected void installProxies(EObject obj, IDiagnosticProducer producer,
			Multimap<EStructuralFeature.Setting, INode> settingsToLink) {
		installProxiesTask.start();
		ICompositeNode node = NodeModelUtils.getNode(obj);
		if (node == null)
			return;
		installProxies(obj, producer, settingsToLink, node);
		installProxiesTask.stop();
	}

	private void installProxies(EObject obj, IDiagnosticProducer producer,
			Multimap<EStructuralFeature.Setting, INode> settingsToLink, ICompositeNode parentNode) {
		final EClass eClass = obj.eClass();
		if (eClass.getEAllReferences().size() - eClass.getEAllContainments().size() == 0)
			return;

		for (Iterator<INode> iterator = parentNode.getChildren().iterator(); iterator.hasNext(); ) {
			INode node = iterator.next();
			if (node.getGrammarElement() instanceof CrossReference && !Iterables.isEmpty(node.getLeafNodes())) {
				CrossReference ref = (CrossReference) node.getGrammarElement();
				producer.setNode(node);
				final EReference eRef = GrammarUtil.getReference(ref, eClass);
				if (eRef == null) {
					throw new IllegalStateException("Couldn't find EReference for crossreference " + ref);
				}
				if (!eRef.isResolveProxies() /*|| eRef.getEOpposite() != null see https://bugs.eclipse.org/bugs/show_bug.cgi?id=282486*/) {
					final EStructuralFeature.Setting setting = ((InternalEObject) obj).eSetting(eRef);
					settingsToLink.put(new SettingDelegate(setting), node);
				} else {
					createAndSetProxy(obj, node, eRef);
				}
			}
		}
		if (shouldCheckParentNode(parentNode)) {
			installProxies(obj, producer, settingsToLink, parentNode.getParent());
		}
	}

	@SuppressWarnings("unchecked")
	protected void installQueuedLinks(Multimap<EStructuralFeature.Setting, INode> settingsToLink) {
		for (EStructuralFeature.Setting setting : settingsToLink.keySet()) {
			final EObject eObject = setting.getEObject();
			final EReference eRef = (EReference) setting.getEStructuralFeature();
			final Collection<INode> nodes = settingsToLink.get(setting);
			if (setting.getEStructuralFeature().isMany()) {
				EList<EObject> list = (EList<EObject>) setting.get(false);
				for (INode node : nodes) {
					final EObject proxy = createProxy(eObject, node, eRef);
					list.add(EcoreUtil.resolve(proxy, eObject));
				}
			} else {
				final INode node = nodes.iterator().next();
				final EObject proxy = createProxy(eObject, node, eRef);
				setting.set(EcoreUtil.resolve(proxy, eObject));
			}
		}
	}

	@SuppressWarnings("unchecked")
{'fraction_non_alphanumeric': 0.13175220709262309, 'fraction_numerical': 0.0025437677689660334, 'mean_word_length': 5.5236700829673016, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 34, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1118699', 'n_tokens_mistral': 4514, 'n_tokens_neox': 4109, 'n_words': 790}