<commit_before>package fr.inria.diverse.melange.lib

import com.google.inject.ImplementedBy
import com.google.inject.Inject
import java.util.List
import org.eclipse.emf.common.util.Diagnostic
import org.eclipse.emf.ecore.EAnnotation
import org.eclipse.emf.ecore.EAttribute
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EDataType
import org.eclipse.emf.ecore.EEnum
import org.eclipse.emf.ecore.EModelElement
import org.eclipse.emf.ecore.ENamedElement
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EOperation
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.EParameter
import org.eclipse.emf.ecore.EReference
import org.eclipse.emf.ecore.EcoreFactory
import org.eclipse.emf.ecore.EcorePackage
import org.eclipse.emf.ecore.util.Diagnostician
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.xtend.lib.annotations.Data

@ImplementedBy(PackageMergeMerger)
interface EcoreMerger {
	/**
	 * Note: assuming that receiving and merged are valid wrt Ecore
	 */
	def EPackage merge(EPackage receiving, EPackage merged)
	def List<EPackage> merge(List<EPackage> receiving, List<EPackage> merged)
	def List<Conflict> getConflicts()

	@Data
	static class Conflict {
		EObject receiving
		EObject merged
		EReference feature
		String message
		Diagnostic emfDiagnostic
	}
}

/**
 * Inspired from UML2.5 PackageMerge specification,
 * as refined by Dingel et al.
 * "Understanding and improving UML package merge"
 * 
 *   - Receiving package and resulting package are *different*. The
 *     receiving package does not play a dual role. Thus, the constraints
 *     on cycle detection do not make sense.
 *   - Ignores generics
 *   - Ignores constraints
 *   - The resulting EPackage is checked using Diagnostician
 *   - All elements inserted from the merged package are marked with an annotation
 */
class PackageMergeMerger implements EcoreMerger {
	@Inject extension EcoreExtensions
	List<Conflict> conflicts
	static final String ORIGIN_ANNOTATION_SOURCE = "http://www.inria.fr/melange/origin"

	override merge(EPackage receiving, EPackage merged) {
		return merge(receiving, merged, true)
	}

	def EPackage merge(EPackage receiving, EPackage merged, boolean validate) {
		conflicts = newArrayList
		val resulting = receiving//EcoreUtil::copy(receiving)

		if (receiving == merged) {
			addConflict(receiving, merged, null, "Cannot merge package with itself")
			return null
		}
		if (receiving === null || merged === null)
			return null
//		if (receiving == merged || receiving.nsURI == merged.nsURI)
//			addConflict(receiving, merged, null, "Cannot merge packages with same URI")
		if (receiving.allSubPkgs.contains(merged))
			addConflict(receiving, merged, null, "Receiving package cannot contain merged package")
		if (merged.allSubPkgs.contains(receiving))
			addConflict(receiving, merged, null, "Merged package cannot contain receiving package")
		// FIXME: We should check for forbidden cross-refs between receiving/merged
		//         but this is quite costly

		if (receiving.doMatch(merged))
			resulting.doMerge(merged)

		if (!conflicts.empty)
			return null

		resulting.updateReferences

		if (validate)
			if (!validateAndProduceConflicts(resulting))
				return null

		resulting.cleanAnnotations

		return resulting
	}

	def void cleanAnnotations(EPackage pkg) {
		val toRemove = newArrayList
		val i = pkg.eAllContents

		while (i.hasNext) {
			val o = i.next<commit_msg>Always clean generated annotations before leaving EcoreMerger<commit_after>package fr.inria.diverse.melange.lib

import com.google.inject.ImplementedBy
import com.google.inject.Inject
import java.util.List
import org.eclipse.emf.common.util.Diagnostic
import org.eclipse.emf.ecore.EAnnotation
import org.eclipse.emf.ecore.EAttribute
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EDataType
import org.eclipse.emf.ecore.EEnum
import org.eclipse.emf.ecore.EModelElement
import org.eclipse.emf.ecore.ENamedElement
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EOperation
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.EParameter
import org.eclipse.emf.ecore.EReference
import org.eclipse.emf.ecore.EcoreFactory
import org.eclipse.emf.ecore.EcorePackage
import org.eclipse.emf.ecore.util.Diagnostician
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.xtend.lib.annotations.Data

@ImplementedBy(PackageMergeMerger)
interface EcoreMerger {
	/**
	 * Note: assuming that receiving and merged are valid wrt Ecore
	 */
	def EPackage merge(EPackage receiving, EPackage merged)
	def List<EPackage> merge(List<EPackage> receiving, List<EPackage> merged)
	def List<Conflict> getConflicts()

	@Data
	static class Conflict {
		EObject receiving
		EObject merged
		EReference feature
		String message
		Diagnostic emfDiagnostic
	}
}

/**
 * Inspired from UML2.5 PackageMerge specification,
 * as refined by Dingel et al.
 * "Understanding and improving UML package merge"
 * 
 *   - Receiving package and resulting package are *different*. The
 *     receiving package does not play a dual role. Thus, the constraints
 *     on cycle detection do not make sense.
 *   - Ignores generics
 *   - Ignores constraints
 *   - The resulting EPackage is checked using Diagnostician
 *   - All elements inserted from the merged package are marked with an annotation
 */
class PackageMergeMerger implements EcoreMerger {
	@Inject extension EcoreExtensions
	List<Conflict> conflicts
	static final String ORIGIN_ANNOTATION_SOURCE = "http://www.inria.fr/melange/origin"

	override merge(EPackage receiving, EPackage merged) {
		return merge(receiving, merged, true)
	}

	def EPackage merge(EPackage receiving, EPackage merged, boolean validate) {
		conflicts = newArrayList
		val resulting = receiving//EcoreUtil::copy(receiving)

		if (receiving == merged) {
			addConflict(receiving, merged, null, "Cannot merge package with itself")
			return null
		}
		if (receiving === null || merged === null)
			return null
//		if (receiving == merged || receiving.nsURI == merged.nsURI)
//			addConflict(receiving, merged, null, "Cannot merge packages with same URI")
		if (receiving.allSubPkgs.contains(merged))
			addConflict(receiving, merged, null, "Receiving package cannot contain merged package")
		if (merged.allSubPkgs.contains(receiving))
			addConflict(receiving, merged, null, "Merged package cannot contain receiving package")
		// FIXME: We should check for forbidden cross-refs between receiving/merged
		//         but this is quite costly

		if (receiving.doMatch(merged))
			resulting.doMerge(merged)

		if (!conflicts.empty) {
			resulting.cleanAnnotations
			return null
		}

		resulting.updateReferences

		val isValid =
			if (validate) validateAndProduceConflicts(resulting)
			else true

		resulting.cleanAnnotations

		return
			if (isValid) resulting
			else null
	}

	def void cleanAnnotations(EPackage pkg) {
		val toRemove = newArrayList
		val i = pkg.eAllContents

		while (i.hasNext) {
			val o = i.next
{'fraction_non_alphanumeric': 0.08631371444663476, 'fraction_numerical': 0.000563221627710504, 'mean_word_length': 4.4764841942945255, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '3528684', 'n_tokens_mistral': 2346, 'n_tokens_neox': 2140, 'n_words': 603}