<commit_before>package io.gitlab.arturbosch.smartsmells.out

import io.gitlab.arturbosch.smartsmells.api.SmellResult
import io.gitlab.arturbosch.smartsmells.common.DetectionResult
import io.gitlab.arturbosch.smartsmells.config.Smell
import io.gitlab.arturbosch.smartsmells.util.Strings

import java.lang.reflect.Field

/**
 * @author artur
 */
class XMLWriter {

	static String toXml(SmellResult smellResult) {

		List<String> entries = new ArrayList<>()
		smellResult.smellSets.each { key, value ->
			entries.addAll(value.collect {
				if (key == Smell.COMPLEX_METHOD || key == Smell.LONG_PARAM) {
					"\t" + handleLongMethodDelegates(it)
				} else if (key == Smell.CYCLE) {
					"\t" + handleDependencyDelegates(it)
				} else {
					"\t" + toXmlEntry(it)
				}
			})
		}

		return "<SmartSmells>\n" + entries.join("\n") + "\n</SmartSmells>"
	}

	private static String handleDependencyDelegates(DetectionResult smelly) {
		def name = smelly.class.simpleName
		def source = (DetectionResult) extractField(smelly, "source")
		def target = (DetectionResult) extractField(smelly, "target")

		def sourceEntry = "<Source " + Strings.substringAfter(toXmlEntry(source), " ")
		def targetEntry = "<Target " + Strings.substringAfter(toXmlEntry(target), " ")
		return "<$name>$sourceEntry$targetEntry</$name>"
	}

	private static Object extractField(DetectionResult smelly, String fieldName) {
		smelly.class.getDeclaredField(fieldName).with {
			setAccessible(true)
			get(smelly)
		}
	}

	private static String handleLongMethodDelegates(DetectionResult smelly) {

		def name = smelly.class.simpleName
		def longMethod = extractField(smelly, "longMethod")

		String attributes = getAttributesFromFields(smelly)

		def base = toXmlEntry((DetectionResult) longMethod)
		def appendStart = "<$name " + Strings.substringAfter(base, " ")
		def appendEnd = Strings.substringBefore(appendStart, "/>") + "$attributes/>"
		return appendEnd
	}

	private static String getAttributesFromFields(DetectionResult smelly) {
		List<Field> fields = extractFields(smelly)
		def entries = joinFieldsToNameValueMap(fields, smelly)
		joinToXmlAttribute(entries)
	}

	static String toXmlEntry(DetectionResult smelly) {

		def name = smelly.class.simpleName
		def path = extractPath(smelly)
		def range = extractSourceRange(smelly)

		List<Field> fields = extractFields(smelly)
		def entries = joinFieldsToNameValueMap(fields, smelly)
		entries << path << range

		return "<$name " + joinToXmlAttribute(entries) + "/>"
	}

	private static List<Field> extractFields(DetectionResult smelly) {
		Arrays.stream(smelly.class.declaredFields)
				.filter { !it.synthetic }
				.filter { it.name != "sourceRange" }
				.filter { it.name != "sourcePath" }
				.filter { it.name != "longMethod" }
				.collect()
	}

	private static Map<String, String> joinFieldsToNameValueMap(List<Field> fields, smelly) {
		fields.collectEntries() {
			it.setAccessible(true)
			[it.name, replaceXmlSymbols(it.get(smelly).toString())]
		}
	}

	private static String replaceXmlSymbols(String value) {
		return value.replaceAll("&", "&amp;")
				.replaceAll("\"", "&quot;")
				.replaceAll("\'", "&apos;")
				.replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;")
	}

	private static String joinToXmlAttribute(Map<String, String> entries) {
		String content = ""
		entries.each { key, value ->
			content += """$key="$value" """
		}
		content
	}

	private static LinkedHashMap<String, String> extractSourceRange(DetectionResult smelly) {
		smelly.class.getDeclaredField("sourceRange").with {
			setAccessible(true)
			def pos = get(smelly).toString().split(',')
			["startLine": pos[0], "startColumn": pos[1], "endLine": pos[2], "endColumn": pos[3]]
		}
	}

	private static LinkedHashMap<String, String> extractPath(DetectionResult smelly) {
		smelly.class.getDeclaredField("sourcePath").with {
			setAccessible(true)
			["path": get(smelly).toString()]
		}
	}
}<commit_msg>Remove class name from SourceRange toString to generate right startLine xml node - Fixes#61<commit_after>package io.gitlab.arturbosch.smartsmells.out

import io.gitlab.arturbosch.smartsmells.api.SmellResult
import io.gitlab.arturbosch.smartsmells.common.DetectionResult
import io.gitlab.arturbosch.smartsmells.config.Smell
import io.gitlab.arturbosch.smartsmells.util.Strings

import java.lang.reflect.Field

/**
 * @author artur
 */
class XMLWriter {

	static String toXml(SmellResult smellResult) {

		List<String> entries = new ArrayList<>()
		smellResult.smellSets.each { key, value ->
			entries.addAll(value.collect {
				if (key == Smell.COMPLEX_METHOD || key == Smell.LONG_PARAM) {
					"\t" + handleLongMethodDelegates(it)
				} else if (key == Smell.CYCLE) {
					"\t" + handleDependencyDelegates(it)
				} else {
					"\t" + toXmlEntry(it)
				}
			})
		}

		return "<SmartSmells>\n" + entries.join("\n") + "\n</SmartSmells>"
	}

	private static String handleDependencyDelegates(DetectionResult smelly) {
		def name = smelly.class.simpleName
		def source = (DetectionResult) extractField(smelly, "source")
		def target = (DetectionResult) extractField(smelly, "target")

		def sourceEntry = "<Source " + Strings.substringAfter(toXmlEntry(source), " ")
		def targetEntry = "<Target " + Strings.substringAfter(toXmlEntry(target), " ")
		return "<$name>$sourceEntry$targetEntry</$name>"
	}

	private static Object extractField(DetectionResult smelly, String fieldName) {
		smelly.class.getDeclaredField(fieldName).with {
			setAccessible(true)
			get(smelly)
		}
	}

	private static String handleLongMethodDelegates(DetectionResult smelly) {

		def name = smelly.class.simpleName
		def longMethod = extractField(smelly, "longMethod")

		String attributes = getAttributesFromFields(smelly)

		def base = toXmlEntry((DetectionResult) longMethod)
		def appendStart = "<$name " + Strings.substringAfter(base, " ")
		def appendEnd = Strings.substringBefore(appendStart, "/>") + "$attributes/>"
		return appendEnd
	}

	private static String getAttributesFromFields(DetectionResult smelly) {
		List<Field> fields = extractFields(smelly)
		def entries = joinFieldsToNameValueMap(fields, smelly)
		joinToXmlAttribute(entries)
	}

	static String toXmlEntry(DetectionResult smelly) {

		def name = smelly.class.simpleName
		def path = extractPath(smelly)
		def range = extractSourceRange(smelly)

		List<Field> fields = extractFields(smelly)
		def entries = joinFieldsToNameValueMap(fields, smelly)
		entries << path << range

		return "<$name " + joinToXmlAttribute(entries) + "/>"
	}

	private static List<Field> extractFields(DetectionResult smelly) {
		Arrays.stream(smelly.class.declaredFields)
				.filter { !it.synthetic }
				.filter { it.name != "sourceRange" }
				.filter { it.name != "sourcePath" }
				.filter { it.name != "longMethod" }
				.collect()
	}

	private static Map<String, String> joinFieldsToNameValueMap(List<Field> fields, smelly) {
		fields.collectEntries() {
			it.setAccessible(true)
			[it.name, replaceXmlSymbols(it.get(smelly).toString())]
		}
	}

	private static String replaceXmlSymbols(String value) {
		return value.replaceAll("&", "&amp;")
				.replaceAll("\"", "&quot;")
				.replaceAll("\'", "&apos;")
				.replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;")
	}

	private static String joinToXmlAttribute(Map<String, String> entries) {
		String content = ""
		entries.each { key, value ->
			content += """$key="$value" """
		}
		content
	}

	private static LinkedHashMap<String, String> extractSourceRange(DetectionResult smelly) {
		smelly.class.getDeclaredField("sourceRange").with {
			setAccessible(true)
			def pos = get(smelly).toString().replace("SourceRange(", "").replace(")", "").split(',')
			["startLine": pos[0], "startColumn": pos[1], "endLine": pos[2], "endColumn": pos[3]]
		}
	}

	private static LinkedHashMap<String, String> extractPath(DetectionResult smelly) {
		smelly.class.getDeclaredField("sourcePath").with {
			setAccessible(true)
			["path": get(smelly).toString()]
		}
	}
}
{'fraction_non_alphanumeric': 0.13921346902877246, 'fraction_numerical': 0.0012564392511622063, 'mean_word_length': 4.706093189964157, 'pattern_counts': {'":': 10, '<': 49, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, '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': '4741384', 'n_tokens_mistral': 2854, 'n_tokens_neox': 2585, 'n_words': 498}