<commit_before>package de.japkit.el

import com.google.common.base.Stopwatch
import de.japkit.services.ElementsExtensions
import de.japkit.services.ExtensionRegistry
import de.japkit.services.MessageCollector
import de.japkit.services.TypeElementNotFoundException
import de.japkit.services.TypesExtensions
import java.io.Writer
import java.net.URL
import java.util.Map
import java.util.ServiceLoader
import java.util.concurrent.TimeUnit
import javax.lang.model.element.Element
import org.eclipse.xtext.xbase.lib.Functions.Function0

import static de.japkit.util.MoreCollectionExtensions.*
import de.japkit.services.RuleException
import de.japkit.services.ReportedException
import java.util.List

class ELSupport {
	val transient extension ElementsExtensions elements = ExtensionRegistry.get(ElementsExtensions)

	extension TypesExtensions types = ExtensionRegistry.get(TypesExtensions)
	val transient extension MessageCollector = ExtensionRegistry.get(MessageCollector)

	val Map<String, ELProvider> elProviders = newHashMap

	val Map<String, ELProvider> templateProviders = newHashMap

	val ELProvider defaultElProvider

	val defaultLanguage = "JavaEL"

	//The tread local value stack.
	def getValueStack() {
		ExtensionRegistry.get(ValueStack, [|new ValueStack(newHashMap("elements" -> elements, "types" -> types), null)])
	}
	

	new() {
		val sw = Stopwatch.createStarted
		try {
			printDiagnosticMessage[
				'''Searching EL-Providers using classloader «ELProvider.classLoader». Context CL: «Thread.currentThread.
					contextClassLoader»''']
			ServiceLoader.load(ELProvider, ELProvider.classLoader).forEach [
				val provider = it
				provider.supportedLanguages.forEach [
					elProviders.put(it, provider)
				]
				provider.supportedTemplateLanguages.forEach [
					templateProviders.put(it, provider)
				]
			]

		} catch (Throwable t) {
			throw new RuntimeException('''Error when loading EL-Providers: «t.message»''', t)
		}

		printDiagnosticMessage['''Init EL-Providers: «sw.elapsed(TimeUnit.MILLISECONDS)»''']
		sw.stop

		defaultElProvider = elProviders.get(defaultLanguage)
	}

	def <T> T scope(Object e, (ValueStack)=>T closure) {
		val vs = valueStack
		try {
			vs.push
			vs.put("src", e)
			closure.apply(vs)

		} finally {
			vs.pop
		}
	}
	
	def <T> T scope((ValueStack)=>T closure) {
		val vs = valueStack
		try {
			vs.push
			closure.apply(vs)

		} finally {
			vs.pop
		}
	}

//	def void scope( Object e, (ValueStack)=>void closure) {
//		val vs = valueStack
//		try {
//			vs.push
//			vs.put("src", e)
//			closure.apply(vs)
//
//		} finally {
//			vs.pop
//		}
//	}
//	
//	def void scope((ValueStack)=>void closure) {
//		val vs = valueStack
//		try {
//			vs.push
//			closure.apply(vs)
//
//		} finally {
//			vs.pop
//		}
//	}
	
	def getCurrentSrcElement() {
		getCurrentSrc(Element)
	}
	
	def <T> T getCurrentSrc(Class<T> clazz){
		val currSrc = getCurrentSrc
		if(currSrc !== null && !clazz.isInstance(currSrc)){
			throw new RuleException('''Current src «currSrc» is of type «currSrc?.class», but type «clazz» is required here.''');
		}
		clazz.cast(currSrc)
	}
	
	def Object getCurrentSrc(){
		valueStack.getRequired("src")
	}
	
	def Object getCurrentSrcOptional(){
		valueStack.get("src")
	}	
	
	def Element getNearestSrcElement(){
		valueStack.nearestSrcElement
	}
	
	def Element getNearestSrcElement(ValueStack vs){
		val src = vs.get("src")
		if(src instanceof Element) src else vs.parent?.nearestSrcElement
	}

	def <T extends Object> T eval(String expr, String lang, Class<T> expectedType, CharSequence errorMessage,
		T errorResult) {
		try {			
			eval(expr, lang, expectedType, true)
		} catch (TypeElementNotFoundException tenfe) {
			throw tenfe
		} catch(ReportedException e){
			//Do not report the error again to avoid error flooding
			errorResult
		} catch (Exception e) {
			reportRuleError(e)
			if(errorResult !== null) return errorResult else throw new ReportedException(e)
		}
	}
	
	//If the expression language is not set, look on value stack at first
	//TODO: only for legal Java identifiers?
	def private <T> T evalFromValueStack(String expr, String lang,  Class<T> expectedType) {
		if(lang.nullOrEmpty){
			val v = valueStack.get(expr)
			val result = if(v instanceof Function0<?>){
				v.apply
			} else {
				v 
			}
			if(result !== null && !expectedType.isInstance(result)){
				throw new ELProviderException('''Value or function «expr» on values stack is not of expected type «expectedType», but «result.class»''')
			}
			result as T
		} else null
	}

	def <T> T eval(String expr, String lang, Class<T> expectedType) {
		eval(expr, lang, expectedType, false)
	}
	
	def <T> T eval(String expr, String lang, Class<T> expectedType, boolean evalFromValueStackFirst) {
		eval(expr, lang, expectedType, evalFromValueStackFirst, EMPTY_EL_IMPORTS)
	}
	
	public static ELImports EMPTY_EL_IMPORTS = new ELImports(emptyList);
	
	def <T> T eval(String expr, String lang, Class<T> expectedType, boolean evalFromValueStackFirst, ELImports elImports) {
		if(evalFromValueStackFirst){
			val resultFromValueStack = evalFromValueStack(expr, lang, expectedType)
			if(resultFromValueStack !== null) return resultFromValueStack
		}
		
		if(expr.nullOrEmpty && expectedType == String){
			return ("" as Object) as T //WTF!?!
		}<commit_msg>closes #22: Support Java EL 3 imports <commit_after>package de.japkit.el

import com.google.common.base.Stopwatch
import de.japkit.services.ElementsExtensions
import de.japkit.services.ExtensionRegistry
import de.japkit.services.MessageCollector
import de.japkit.services.TypeElementNotFoundException
import de.japkit.services.TypesExtensions
import java.io.Writer
import java.net.URL
import java.util.Map
import java.util.ServiceLoader
import java.util.concurrent.TimeUnit
import javax.lang.model.element.Element
import org.eclipse.xtext.xbase.lib.Functions.Function0

import static de.japkit.util.MoreCollectionExtensions.*
import de.japkit.services.RuleException
import de.japkit.services.ReportedException
import java.util.List
import de.japkit.services.GenerateClassContext
import de.japkit.model.AnnotationAndParent
import de.japkit.rules.LibraryRule

class ELSupport {
	val transient extension ElementsExtensions elements = ExtensionRegistry.get(ElementsExtensions)

	extension TypesExtensions types = ExtensionRegistry.get(TypesExtensions)
	val transient extension MessageCollector = ExtensionRegistry.get(MessageCollector)

	val Map<String, ELProvider> elProviders = newHashMap

	val Map<String, ELProvider> templateProviders = newHashMap

	val ELProvider defaultElProvider

	val defaultLanguage = "JavaEL"

	//The tread local value stack.
	def getValueStack() {
		ExtensionRegistry.get(ValueStack, [|new ValueStack(newHashMap("elements" -> elements, "types" -> types), null)])
	}
	

	new() {
		val sw = Stopwatch.createStarted
		try {
			printDiagnosticMessage[
				'''Searching EL-Providers using classloader «ELProvider.classLoader». Context CL: «Thread.currentThread.
					contextClassLoader»''']
			ServiceLoader.load(ELProvider, ELProvider.classLoader).forEach [
				val provider = it
				provider.supportedLanguages.forEach [
					elProviders.put(it, provider)
				]
				provider.supportedTemplateLanguages.forEach [
					templateProviders.put(it, provider)
				]
			]

		} catch (Throwable t) {
			throw new RuntimeException('''Error when loading EL-Providers: «t.message»''', t)
		}

		printDiagnosticMessage['''Init EL-Providers: «sw.elapsed(TimeUnit.MILLISECONDS)»''']
		sw.stop

		defaultElProvider = elProviders.get(defaultLanguage)
	}

	def <T> T scope(Object e, (ValueStack)=>T closure) {
		val vs = valueStack
		try {
			vs.push
			vs.put("src", e)
			closure.apply(vs)

		} finally {
			vs.pop
		}
	}
	
	def <T> T scope((ValueStack)=>T closure) {
		val vs = valueStack
		try {
			vs.push
			closure.apply(vs)

		} finally {
			vs.pop
		}
	}

//	def void scope( Object e, (ValueStack)=>void closure) {
//		val vs = valueStack
//		try {
//			vs.push
//			vs.put("src", e)
//			closure.apply(vs)
//
//		} finally {
//			vs.pop
//		}
//	}
//	
//	def void scope((ValueStack)=>void closure) {
//		val vs = valueStack
//		try {
//			vs.push
//			closure.apply(vs)
//
//		} finally {
//			vs.pop
//		}
//	}
	
	def getCurrentSrcElement() {
		getCurrentSrc(Element)
	}
	
	def <T> T getCurrentSrc(Class<T> clazz){
		val currSrc = getCurrentSrc
		if(currSrc !== null && !clazz.isInstance(currSrc)){
			throw new RuleException('''Current src «currSrc» is of type «currSrc?.class», but type «clazz» is required here.''');
		}
		clazz.cast(currSrc)
	}
	
	def Object getCurrentSrc(){
		valueStack.getRequired("src")
	}
	
	def Object getCurrentSrcOptional(){
		valueStack.get("src")
	}	
	
	def Element getNearestSrcElement(){
		valueStack.nearestSrcElement
	}
	
	def Element getNearestSrcElement(ValueStack vs){
		val src = vs.get("src")
		if(src instanceof Element) src else vs.parent?.nearestSrcElement
	}

	def <T extends Object> T eval(String expr, String lang, Class<T> expectedType, CharSequence errorMessage,
		T errorResult) {
		try {			
			eval(expr, lang, expectedType, true)
		} catch (TypeElementNotFoundException tenfe) {
			throw tenfe
		} catch(ReportedException e){
			//Do not report the error again to avoid error flooding
			errorResult
		} catch (Exception e) {
			reportRuleError(e)
			if(errorResult !== null) return errorResult else throw new ReportedException(e)
		}
	}
	
	//If the expression language is not set, look on value stack at first
	//TODO: only for legal Java identifiers?
	def private <T> T evalFromValueStack(String expr, String lang,  Class<T> expectedType) {
		if(lang.nullOrEmpty){
			val v = valueStack.get(expr)
			val result = if(v instanceof Function0<?>){
				v.apply
			} else {
				v 
			}
			if(result !== null && !expectedType.isInstance(result)){
				throw new ELProviderException('''Value or function «expr» on values stack is not of expected type «expectedType», but «result.class»''')
			}
			result as T
		} else null
	}

	def <T> T eval(String expr, String lang, Class<T> expectedType) {
		eval(expr, lang, expectedType, false)
	}
	
	def <T> T eval(String expr, String lang, Class<T> expectedType, boolean evalFromValueStackFirst) {
		eval(expr, lang, expectedType, evalFromValueStackFirst, findElImports())
	}
	
	//find current Meta-Element and determine ElImports of it. 
	//TODO: Should be done in rule package instead. (Factoring out some ExpressionRule, like already done for other rules. See ExpressionOrFunctionCallRule) 
	private def findElImports() {
		val extension GenerateClassContext = ExtensionRegistry.get(GenerateClassContext);
		val rule = currentRule
		val metaAnnotation = rule.metaAnnotation;
		var metaElement = if (metaAnnotation instanceof AnnotationAndParent) metaAnnotation?.rootAnnotatedElement else rule?.metaElement
		return if(metaElement !== null) LibraryRule.findELImports(metaElement) else EMPTY_EL_IMPORTS
	}
	
	public static ELImports EMPTY_EL_IMPORTS = new ELImports(emptyList);
	
	def <T> T eval(String expr, String lang, Class<T> expectedType, boolean evalFromValueStackFirst, ELImports elImports) {
		if(evalFromValueStackFirst){
			val resultFromValueStack = evalFromValueStack(expr, lang, expectedType)
			if(resultFromValueStack !== null) return resultFromValueStack
		}
		
		if(expr.nullOrEmpty && expectedType == String){
			return ("" as Object) as T //WTF!?!
		}
{'fraction_non_alphanumeric': 0.11184442097852869, 'fraction_numerical': 0.0006159802886307638, 'mean_word_length': 4.16356201726488, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '6890453', 'n_tokens_mistral': 3964, 'n_tokens_neox': 3581, 'n_words': 957}