-<!--- PUBLIC MODEL INITIALIZATION METHODS --->
-
-<cffunction name="automaticValidations" returntype="void" access="public" output="false">
-	<cfargument name="value" type="boolean" required="true">
-	<cfscript>
+<cfscript>
+	/*
+	* PUBLIC MODEL INITIALIZATION METHODS
+	*/
+
+	public void function automaticValidations(required boolean value) {
 		variables.wheels.class.automaticValidations = arguments.value;
-	</cfscript>
-</cffunction>
-
-<cffunction name="validate" returntype="void" access="public" output="false">
-	<cfargument name="methods" type="string" required="false" default="">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfargument name="when" type="string" required="false" default="onSave">
-	<cfscript>
+	}
+
+	public void function validate(
+		string methods="",
+		string condition="",
+		string unless="",
+		string when="onSave"
+	) {
 		$registerValidation(argumentCollection=arguments);
-	</cfscript>
-</cffunction>
-
-<cffunction name="validateOnCreate" returntype="void" access="public" output="false">
-	<cfargument name="methods" type="string" required="false" default="">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfscript>
+	}
+
+	public void function validateOnCreate(
+		string methods="",
+		string condition="",
+		string unless=""
+	) {
 		$registerValidation(when="onCreate", argumentCollection=arguments);
-	</cfscript>
-</cffunction>
-
-<cffunction name="validateOnUpdate" returntype="void" access="public" output="false">
-	<cfargument name="methods" type="string" required="false" default="">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfscript>
+	}
+
+	public void function validateOnUpdate(
+		string methods="",
+		string condition="",
+		string unless=""
+	) {
 		$registerValidation(when="onUpdate", argumentCollection=arguments);
-	</cfscript>
-</cffunction>
-
-<cffunction name="validatesConfirmationOf" returntype="void" access="public" output="false">
-	<cfargument name="properties" type="string" required="false" default="">
-	<cfargument name="message" type="string" required="false">
-	<cfargument name="when" type="string" required="false" default="onSave">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfscript>
+	}
+
+	public void function validatesConfirmationOf(
+		string properties="",
+		string message,
+		string when="onSave",
+		string condition="",
+		string unless=""
+	) {
 		$args(name="validatesConfirmationOf", args=arguments);
 		$registerValidation(methods="$validatesConfirmationOf", argumentCollection=arguments);
-	</cfscript>
-</cffunction>
-
-<cffunction name="validatesExclusionOf" returntype="void" access="public" output="false">
-	<cfargument name="properties" type="string" required="false" default="">
-	<cfargument name="list" type="string" required="true">
-	<cfargument name="message" type="string" required="false">
-	<cfargument name="when" type="string" required="false" default="onSave">
-	<cfargument name="allowBlank" type="boolean" required="false">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfscript>
+	}
+
+	public void function validatesExclusionOf(
+		string properties="",
+		required string list,
+		string message,
+		string when="onSave",
+		boolean allowBlank,
+		string condition="",
+		string unless=""
+	) {
 		$args(name="validatesExclusionOf", args=arguments);
 		arguments.list = $listClean(arguments.list);
 		$registerValidation(methods="$validatesExclusionOf", argumentCollection=arguments);
-	</cfscript>
-</cffunction>
-
-<cffunction name="validatesFormatOf" returntype="void" access="public" output="false">
-	<cfargument name="properties" type="string" required="false" default="">
-	<cfargument name="regEx" type="string" required="false" default="">
-	<cfargument name="type" type="string" required="false" default="">
-	<cfargument name="message" type="string" required="false">
-	<cfargument name="when" type="string" required="false" default="onSave">
-	<cfargument name="allowBlank" type="boolean" required="false">
-	<cfargument name="condition" type="string" required="false" default="">
-	<cfargument name="unless" type="string" required="false" default="">
-	<cfscript>
+	}
+
+	public void function validatesFormatOf(
+		string properties="",
+		string regEx="",
+		string type="",
+		string message,
+		string when="onSave",
+		boolean allowBlank,
+		string condition="",
+		string unless=""
+	) {
 		$args(name="validatesFormatOf", args=arguments);
 		if (application.wheels.showErrorInformation)
 		{

		}

		// loop over the passed in types
		loc.iEnd = ListLen(arguments.type);
		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
		{
			loc.item = ListGetAt(arguments.type, loc.i);

			// loop through all validations for passed in type (onSave, onCreate etc) that has been set on this model object
			loc.jEnd = ArrayLen(variables.wheels.class.validations[loc.item]);
			for (loc.j=1; loc.j <= loc.jEnd; loc.j++)
			{
				loc.thisValidation = variables.wheels.class.validations[loc.item][loc.j];
				if ($evaluateCondition(argumentCollection=loc.thisValidation.args))
				{
					if (loc.thisValidation.method == "$validatesPresenceOf")
					{
						// if the property does not exist or if it's blank we add an error on the object (for all other validation types we call corresponding methods below instead)
						if (!StructKeyExists(this, loc.thisValidation.args.property) || (IsSimpleValue(this[loc.thisValidation.args.property]) && !Len(Trim(this[loc.thisValidation.args.property]))) || (IsStruct(this[loc.thisValidation.args.property]) && !StructCount(this[loc.thisValidation.args.property])))
						{
							addError(property=loc.thisValidation.args.property, message=$validationErrorMessage(loc.thisValidation.args.property, loc.thisValidation.args.message));
						}
					}
					else
					{
						// if the validation set does not allow blank values we can set an error right away, otherwise we call a method to run the actual check
						if (StructKeyExists(loc.thisValidation.args, "property") && StructKeyExists(loc.thisValidation.args, "allowBlank") && !loc.thisValidation.args.allowBlank && (!StructKeyExists(this, loc.thisValidation.args.property) || (!Len(this[loc.thisValidation.args.property]) && loc.thisValidation.method != "$validatesUniquenessOf")))
						{
							addError(property=loc.thisValidation.args.property, message=$validationErrorMessage(loc.thisValidation.args.property, loc.thisValidation.args.message));
						}
						else if (!StructKeyExists(loc.thisValidation.args, "property") || (StructKeyExists(this, loc.thisValidation.args.property) &&(Len(this[loc.thisValidation.args.property]) || loc.thisValidation.method == "$validatesUniquenessOf")))
						{
							$invoke(method=loc.thisValidation.method, invokeArgs=loc.thisValidation.args);
						}
					}
				}
			}
		}

		// now that we have run all the validation checks we can return "true" if no errors exist on the object, "false" otherwise
		loc.rv = !hasErrors();
	</cfscript>
	<cfreturn loc.rv>
</cffunction>

<cffunction name="$evaluateCondition" returntype="boolean" access="public" output="false" hint="Evaluates the condition to determine if the validation should be executed.">
	<cfscript>
		var loc = {};
		loc.rv = false;

		// since cf8 can't handle cfscript operators (==, != etc) inside an Evaluate() call we replace them with eq, neq etc in a try / catch
		loc.evaluate = "condition,unless";
		loc.iEnd = ListLen(loc.evaluate);
		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
		{
			loc.item = ListGetAt(loc.evaluate, loc.i);
			if (StructKeyExists(arguments, loc.item) && Len(arguments[loc.item]))
			{
				loc.key = loc.item & "Evaluated";
				try
				{
					loc[loc.key] = Evaluate(arguments[loc.item]);
				}
				catch (any e)
				{
					arguments[loc.item] = Replace(ReplaceList(arguments[loc.item], "==,!=,<,<=,>,>=", " eq , neq , lt , lte , gt , gte "), "  ", " ", "all");
					loc[loc.key] = Evaluate(arguments[loc.item]);
				}
			}
		}

		// proceed with validation when "condition" has been supplied and it evaluates to "true" or when "unless" has been supplied and it evaluates to "false"
		// if both "condition" and "unless" have been supplied though, they both need to be evaluated correctly ("true"/false" that is) for validation to proceed
		if ((!StructKeyExists(arguments, "condition") || !Len(arguments.condition) || loc.conditionEvaluated) && (!StructKeyExists(arguments, "unless") || !Len(arguments.unless) || !loc.unlessEvaluated))
		{
			loc.rv = true;
		}
	</cfscript>
	<cfreturn loc.rv>
</cffunction>

<cffunction name="$validatesConfirmationOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesConfirmationOf method.">
	<cfscript>
		var loc = {};
		loc.virtualConfirmProperty = arguments.property & "Confirmation";
		if (StructKeyExists(this, loc.virtualConfirmProperty) && this[arguments.property] != this[loc.virtualConfirmProperty])
		{
			addError(property=loc.virtualConfirmProperty, message=$validationErrorMessage(argumentCollection=arguments));
		}
	</cfscript>
</cffunction>

<cffunction name="$validatesExclusionOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesExclusionOf method.">
	<cfscript>
		if (ListFindNoCase(arguments.list, this[arguments.property]))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	</cfscript>
</cffunction>

<cffunction name="$validatesFormatOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesFormatOf method.">
	<cfscript>
		if ((Len(arguments.regEx) && !REFindNoCase(arguments.regEx, this[arguments.property])) || (Len(arguments.type) && !IsValid(arguments.type, this[arguments.property])))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	</cfscript>
</cffunction>

<cffunction name="$validatesInclusionOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesInclusionOf method.">
	<cfscript>
		if (!ListFindNoCase(arguments.list, this[arguments.property]))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	</cfscript>
</cffunction>

<cffunction name="$validatesPresenceOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesPresenceOf method.">
	<cfargument name="property" type="string" required="true">
	<cfargument name="message" type="string" required="true">
	<cfargument name="properties" type="struct" required="false" default="#this.properties()#">
	<cfscript>
		// if the property does not exist or if it's blank we add an error on the object
		if (!StructKeyExists(arguments.properties, arguments.property) || (IsSimpleValue(arguments.properties[arguments.property]) && !Len(Trim(arguments.properties[arguments.property]))) || (IsStruct(arguments.properties[arguments.property]) && !StructCount(arguments.properties[arguments.property])))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	</cfscript>
</cffunction>

<cffunction name="$validatesLengthOf" returntype="void" access="public" output="false" hint="Adds an error if the object property fail to pass the validation setup in the @validatesLengthOf method.">
	<cfargument name="property" type="string" required="true">
	<cfargument name="message" type="string" required="true">
	<cfargument name="exactly" type="numeric" required="true">
	<cfargument name="maximum" type="numeric" required="true">
	<cfargument name="minimum" type="numeric" required="true">
	<cfargument name="within" type="any" required="true">
	<cfargument name="properties" type="struct" required="false" default="#this.properties()#">
	<cfscript>
		var loc = {};
		loc.lenValue = Len(arguments.properties[arguments.property]);

		// for within, just create minimum / maximum values
		if (IsArray(arguments.within) && ArrayLen(arguments.within) == 2)

			arguments.maximum = arguments.within[2];
		}

		if ((arguments.maximum && local.lenValue > arguments.maximum) || (arguments.minimum && local.lenValue < arguments.minimum) || (arguments.exactly && local.lenValue != arguments.exactly))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	}

	/**
    * Adds an error if the object property fail to pass the validation setup in the @validatesNumericalityOf method.
    */
	public void function $validatesNumericalityOf() {
		if (!IsNumeric(this[arguments.property]) || (arguments.onlyInteger && Round(this[arguments.property]) != this[arguments.property]) || (IsNumeric(arguments.greaterThan) && this[arguments.property] <= arguments.greaterThan) || (IsNumeric(arguments.greaterThanOrEqualTo) && this[arguments.property] < arguments.greaterThanOrEqualTo) || (IsNumeric(arguments.equalTo) && this[arguments.property] != arguments.equalTo) || (IsNumeric(arguments.lessThan) && this[arguments.property] >= arguments.lessThan) || (IsNumeric(arguments.lessThanOrEqualTo) && this[arguments.property] > arguments.lessThanOrEqualTo) || (IsBoolean(arguments.odd) && arguments.odd && !BitAnd(this[arguments.property], 1)) || (IsBoolean(arguments.even) && arguments.even && BitAnd(this[arguments.property], 1)))
		{
			addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
		}
	}

	/**
    * Adds an error if the object property fail to pass the validation setup in the @validatesUniquenessOf method.
    */
	public void function $validatesUniquenessOf(
		required string property,
		required string message,
		string scope="",
		struct properties="#this.properties()#",
		boolean includeSoftDeletes="true"
	) {
		if (!IsBoolean(variables.wheels.class.tableName) || variables.wheels.class.tableName)
		{
			local.where = [];

			// create the WHERE clause to be used in the query that checks if an identical value already exists
			// wrap value in single quotes unless it's numeric
			// example: "userName='Joe'"
			local.part = arguments.property & "=" & variables.wheels.class.adapter.$quoteValue(str=this[arguments.property], type=validationTypeForProperty(arguments.property));
			if (Right(local.part, 3) == "=''" && ListFindNoCase("integer,float,boolean", validationTypeForProperty(arguments.property)))
			{
				// when numeric property but blank we need to translate to IS NULL
				local.part = SpanExcluding(local.part, "=") & " IS NULL";
			}
			ArrayAppend(local.where, local.part);

			// add scopes to the WHERE clause if passed in, this means that checks for other properties are done in the WHERE clause as well
			// example: "userName='Joe'" becomes "userName='Joe' AND account=1" if scope is "account" for example
			arguments.scope = $listClean(arguments.scope);
			local.iEnd = ListLen(arguments.scope);
			for (local.i=1; local.i <= local.iEnd; local.i++)
			{
				local.item = ListGetAt(arguments.scope, local.i);
				local.part = local.item & "=" & variables.wheels.class.adapter.$quoteValue(str=this[local.item], type=validationTypeForProperty(local.item));
				if (Right(local.part, 3) == "=''" && ListFindNoCase("integer,float,boolean", validationTypeForProperty(local.item)))
				{
					// when numeric property but blank we need to translate to IS NULL
					local.part = SpanExcluding(local.part, "=") & " IS NULL";
				}
				ArrayAppend(local.where, local.part);
			}

			// try to fetch existing object from the database
			local.existingObject = findOne(select=primaryKey(), where=ArrayToList(local.where, " AND "), reload=true, includeSoftDeletes=arguments.includeSoftDeletes, callbacks=false);

			// we add an error if an object was found in the database and the current object is either not saved yet or not the same as the one in the database
			if (IsObject(local.existingObject) && (isNew() || local.existingObject.key() != key($persisted=true)))
			{
				addError(property=arguments.property, message=$validationErrorMessage(argumentCollection=arguments));
			}
		}
	}

	public boolean function $validationExists(required string property, required string validation) {
			// checks to see if a validation has been created for a property
			local.rv = false;
			for (local.key in variables.wheels.class.validations)
			{
				if (StructKeyExists(variables.wheels.class.validations, local.key))
				{
					local.eventArray = variables.wheels.class.validations[local.key];
					local.iEnd = ArrayLen(local.eventArray);
					for (local.i=1; local.i <= local.iEnd; local.i++)
					{
						if (StructKeyExists(local.eventArray[local.i].args, "property") && local.eventArray[local.i].args.property == arguments.property && local.eventArray[local.i].method == "$#arguments.validation#")
						{
							local.rv = true;
							break;
						}
					}
				}
			}
		return local.rv;
	}
</cfscript>