Commit 0244af2e authored by Daniel Honsel's avatar Daniel Honsel
Browse files

added log validation

parent b0f823d8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ Workflow {
			// composedCheck = "de.ugoe.cs.swe.validation.CodeStyleValidator"
			// composedCheck = "de.ugoe.cs.swe.validation.ModularizationValidator"
			// composedCheck = "de.ugoe.cs.swe.validation.MiscFeatureValidator"
			// composedCheck = "de.ugoe.cs.swe.validation.LogValidator"

			// name clash validator
			// can currently only be used with the eclipse editor, does not work 
+280 −0
Original line number Diff line number Diff line
package de.ugoe.cs.swe.validation

import com.google.common.base.Strings

import de.ugoe.cs.swe.TTCN3Configuration.QualityCheckProfile
import de.ugoe.cs.swe.common.ConfigTools
import de.ugoe.cs.swe.common.MiscTools
import de.ugoe.cs.swe.common.logging.LoggingInterface.LogLevel
import de.ugoe.cs.swe.common.logging.LoggingInterface.MessageClass
import de.ugoe.cs.swe.tTCN3.AltstepDef
import de.ugoe.cs.swe.tTCN3.ControlStatementOrDefList
import de.ugoe.cs.swe.tTCN3.ExtFunctionDef
import de.ugoe.cs.swe.tTCN3.FunctionDef
import de.ugoe.cs.swe.tTCN3.FunctionInstance
import de.ugoe.cs.swe.tTCN3.FunctionStatement
import de.ugoe.cs.swe.tTCN3.FunctionStatementList
import de.ugoe.cs.swe.tTCN3.LogStatement
import de.ugoe.cs.swe.tTCN3.ModuleControlBody
import de.ugoe.cs.swe.tTCN3.PredefinedValue
import de.ugoe.cs.swe.tTCN3.SetLocalVerdict
import de.ugoe.cs.swe.tTCN3.TTCN3Reference
import de.ugoe.cs.swe.tTCN3.TestcaseDef
import de.ugoe.cs.swe.tTCN3.Value
import de.ugoe.cs.swe.tTCN3.VerdictTypeValue

import java.util.regex.Matcher
import java.util.regex.Pattern

import org.eclipse.xtext.nodemodel.INode
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
import org.eclipse.xtext.validation.AbstractDeclarativeValidator
import org.eclipse.xtext.validation.Check
import org.eclipse.xtext.validation.EValidatorRegistrar

import static extension de.ugoe.cs.swe.common.TTCN3ScopeHelper.*
import static extension org.eclipse.xtext.EcoreUtil2.*

class LogValidator extends AbstractDeclarativeValidator {
	val ConfigTools configTools = ConfigTools.getInstance;
	var QualityCheckProfile activeProfile = configTools.selectedProfile as QualityCheckProfile

	@Check
	def checkLogStatementFormat(FunctionStatementList functionStmList) {
		val parentTC = functionStmList.findDesiredParent(TestcaseDef)
		val parentFunc = functionStmList.findDesiredParent(FunctionDef)
		val parentAlt = functionStmList.findDesiredParent(AltstepDef)
		var TTCN3Reference parent = null;

		if (parentTC != null) {
			parent = parentTC;
		} else if (parentFunc != null) {
			parent = parentFunc;
		} else if (parentAlt != null) {
			parent = parentAlt;
		}

		var INode node = null
		var statementToCheck = ""
		var other = false
		for (s : functionStmList.eContents) {
			if (s instanceof FunctionStatement) {
				val log = s.findLogStatement
				if (log != null) {
					node = NodeModelUtils.getNode(log)
					if (other) {
						if (!Strings.isNullOrEmpty(statementToCheck)) {
							statementToCheck.checkLogStatementFormat(node, parent)
						}
						other = false
						statementToCheck = ""
					}
					statementToCheck += log.concatLogItems
					if (!activeProfile.processSubsequentLogStatementsAsOne) {
						statementToCheck.checkLogStatementFormat(node, parent)
						statementToCheck = ""
					}
				} else {
					other = true
				}
			}
		}
		if (!Strings.isNullOrEmpty(statementToCheck)) {
			statementToCheck.checkLogStatementFormat(node, parent)
		}
	}

	@Check
	def checkLogStatementFormat(ModuleControlBody ctrl) {
		var INode node = null
		var statementToCheck = ""
		var other = false
		for (s : ctrl.eContents) {
			if (s instanceof ControlStatementOrDefList) {
				val log = s.findLogStatement
				if (log != null) {
					node = NodeModelUtils.getNode(log)
					if (other) {
						if (!Strings.isNullOrEmpty(statementToCheck)) {
							statementToCheck.checkLogStatementFormat(node, null)
						}
						other = false
						statementToCheck = ""
					}
					statementToCheck += log.concatLogItems
					if (!activeProfile.processSubsequentLogStatementsAsOne) {
						statementToCheck.checkLogStatementFormat(node, null)
						statementToCheck = ""
					}
				} else {
					other = true
				}
			}
		}
		if (!Strings.isNullOrEmpty(statementToCheck)) {
			statementToCheck.checkLogStatementFormat(node, null)
		}
	}

	private def LogStatement findLogStatement(ControlStatementOrDefList stm) {
		if (stm.def.control != null && stm.def.control.basic != null && stm.def.control.basic.log != null) {
			return stm.def.control.basic.log
		} else {
			return null
		}
	}

	private def LogStatement findLogStatement(FunctionStatement stm) {
		if (stm.basic != null && stm.basic.log != null) {
			return stm.basic.log
		} else {
			return null
		}
	}

	private def String concatLogItems(LogStatement log) {
		var logString = ""
		val items = log.getAllContentsOfType(PredefinedValue)
		for (i : items) {
			if (i.charString != null) {
				var String subString = i.charString.toString.subSequence(1, i.charString.toString.length - 1) as String
				logString += subString.replaceAll("\"\"", "\"")
			}
		}
		return logString;
	}

	private def checkLogStatementFormat(String log, INode node, TTCN3Reference parent) {
		var problemOccured = false
		var warning = ""
		val Pattern logPattern = Pattern.compile(activeProfile.getLogFormatRegExp(), Pattern.DOTALL)
		val Matcher logMatcher = logPattern.matcher(log)

		if (!logMatcher.matches()) {
			problemOccured = true;
			warning = "Invalid log format (\"" + log + "\")!";
		} else if (!logMatcher.group(1).equals("") && parent != null) {
			val logItemContainerIdentifier = logMatcher.group(1)
			if (!parent.name.equals(logItemContainerIdentifier)) {
				problemOccured = true;
				warning = "Log statement contains invalid container identifier (\"" + logItemContainerIdentifier +
					"\", should be \"" + parent.name + "\")!";
			}
		}

		if (problemOccured) {
			TTCN3StatisticsProvider.getInstance.incrementCountLog
			warning(
				warning,
				null,
				MessageClass.LOGGING.toString,
				node.startLine.toString,
				node.endLine.toString,
				"5.1, " + MiscTools.getMethodName()
			);
		}
	}

	@Check
	def checkExternalFunctionInvocationPrecededByLogStatement(FunctionInstance function) {
		if (!activeProfile.checkExternalFunctionInvocationPrecededByLogStatement)
			return;

		val INode node = NodeModelUtils.getNode(function)
		var problemsOccured = false

		if (function.ref instanceof ExtFunctionDef) {
			val parent = function.findDesiredParent(FunctionStatement)
			val parentList = function.findDesiredParent(FunctionStatementList)
			if (parent == null) {
				val message = "Call to external function \"" + function.ref.name +
					"\" within a definition on the module level. It cannot be preceded by a log statement!"
				info(
					message,
					null,
					MessageClass.LOGGING.toString,
					node.startLine.toString,
					node.endLine.toString,
					"5.3, " + MiscTools.getMethodName(),
					LogLevel.INFORMATION.toString
				);
			} else {
				val items = parentList.eContents
				val idx = items.indexOf(parent)
				if (idx > 0 && idx < items.size) {
					val log = (items.get(idx - 1) as FunctionStatement).findLogStatement
					if (log == null) {
						problemsOccured = true
					}
				} else {
					problemsOccured = true
				}
			}
		}

		if (problemsOccured) {
			TTCN3StatisticsProvider.getInstance.incrementCountLog
			val message = "Call to external function \"" + function.ref.name + "\" not preceded by a log statement!"
			warning(
				message,
				null,
				MessageClass.LOGGING.toString,
				node.startLine.toString,
				node.endLine.toString,
				"5.3, " + MiscTools.getMethodName()
			);
		}
	}

	@Check
	def checkInconcOrFailSetverdictPrecededByLog(SetLocalVerdict verdict) {
		if (!activeProfile.checkInconcOrFailSetVerdictPrecededByLog)
			return;

		val parent = verdict.findDesiredParent(FunctionStatement)
		val parentList = verdict.findDesiredParent(FunctionStatementList)
		val INode node = NodeModelUtils.getNode(verdict)
		var problemsOccured = false
		val exp = verdict.expression
		var isInconcOrFail = false

		if (exp  instanceof Value) {
			if (exp.predef != null && exp.predef.verdictType != null) {
				if (exp.predef.verdictType == VerdictTypeValue.INCONC || exp.predef.verdictType == VerdictTypeValue.FAIL) {
					isInconcOrFail = true
				}
			}
		}

		if (!isInconcOrFail)
			return;

		val items = parentList.eContents
		val idx = items.indexOf(parent)
		if (idx > 0 && idx < items.size) {
			val log = (items.get(idx - 1) as FunctionStatement).findLogStatement
			if (log == null) {
				problemsOccured = true
			}
		} else {
			problemsOccured = true
		}

		if (problemsOccured) {
			TTCN3StatisticsProvider.getInstance.incrementCountLog
			val message = "No log statement precedes a fail or inconc setverdict statement!!"
			warning(
				message,
				null,
				MessageClass.LOGGING.toString,
				node.startLine.toString,
				node.endLine.toString,
				"5.4, " + MiscTools.getMethodName()
			);
		}
	}

	override register(EValidatorRegistrar registrar) {
		//not needed for classes used as ComposedCheck
	}
}