/*	plistschema.plist
	
	Schema for plist schemas. Oooh, meta.
	
	Note: for clarity, quoted strings are used only for strings that will
	appear in the file being checked. This may cause problems under GNUstep.
*/

{
	type = $rootTypeSpecifier;
	
	$definitions =
	{
		$typeSpecifier =
		{
			type = oneOf;
			options =
			(
				$simpleTypeSpecifier,
				$macroInvocation,
				$stringTypeSpecifier,
				$arrayTypeSpecifier,
				$dictionaryTypeSpecifier,
				$numberTypeSpecifier,
				$positiveNumberTypeSpecifier,
				$parameterLessTypeSpecifier,
				$enumerationTypeSpecifier,
				$oneOfTypeSpecifier,
				$delegatedTypeSpecifier
			);
		};
		
		$rootTypeSpecifier =
		{
			type = oneOf;
			options =
			(
				$simpleTypeSpecifier,
				$rootMacroInvocation,
				$stringTypeSpecifier,
				$numberTypeSpecifier,
				$positiveNumberTypeSpecifier,
				$parameterLessTypeSpecifier,
				$enumerationTypeSpecifier,
				$rootArrayTypeSpecifier,
				$rootDictionaryTypeSpecifier,
				$rootOneOfTypeSpecifier,
				$rootDelegatedTypeSpecifier
			);
		};
		
		$simpleTypeSpecifier =
		{
			// Simple types that can be referred to by name with no parameters
			type = enumeration;
			values =
			(
				"string",
				"array",
				"dictionary",
				"integer",
				"positiveInteger",
				"float",
				"positiveFloat",
				"boolean",
				"fuzzyBoolean",
				"vector",
				"quaternion"
				// Not oneOf, enumeration or delegatedType
			);
		};
		
		$macroInvocation =
		{
			type = oneOf;
			options =
			(
				$macroInvocationString,
				{
					type = dictionary;
					schema =
					{
						"type" = $macroInvocation;
					};
					allowOthers = NO;
					requiredKeys = ( "type" );
				}
			);
		};
		$macroInvocationString =
		{
			type = string;
			minLength = 2;
			requiredPrefix = "$";
		};
		
		
		$stringTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "string" );
				};
				
				"filter" = $filterSpecifier;
				"requiredPrefix" = $stringOrStringArray;
				"requiredSuffix" = $stringOrStringArray;
				"requiredSubString" = $stringOrStringArray;
				"minLength" = positiveInteger;
				"maxLength" = positiveInteger;
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$arrayTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "array" );
				};
				
				"minCount" = positiveInteger;
				"maxCount" = positiveInteger;
				"valueType" = $typeSpecifier;
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$dictionaryTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "dictionary" );
				};
				
				"minCount" = positiveInteger;
				"maxCount" = positiveInteger;
				"valueType" = $typeSpecifier;
				"schema" =
				{
					type = dictionary;
					valueType = $typeSpecifier;
				};
				"allowOthers" = boolean;
				"requiredKeys" =
				{
					type = array;
					valueType = string;
				};
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$numberTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values =
					(
						"integer",
						"float"
					);
				};
				
				"minimum" = integer;
				"maximum" = integer;
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$positiveNumberTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values =
					(
						"positiveInteger",
						"positiveFloat"
					);
				};
				
				"minimum" = positiveInteger;
				"maximum" = positiveInteger;
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$parameterLessTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values =
					(
						"boolean",
						"fuzzyBoolean",
						"vector",
						"quaternion"
					);
				};
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$enumerationTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "enumeration" );
				};
				
				"values" =
				{
					type = array;
					valueType = string;
					minCount = 1;
				};
				"filter" = $filterSpecifier;
			};
			allowOthers = NO;
			requiredKeys = ( "type", "values" );
		};
		$oneOfTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "oneOf" );
				};
				
				"options" =
				{
					type = array;
					valueType = $typeSpecifier;
					minCount = 1;
				};
			};
			allowOthers = NO;
			requiredKeys = ( "type", "options" );
		};
		$delegatedTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "delegatedType" );
				};
				
				"baseType" = $typeSpecifier;
				"key" = string;
			};
			allowOthers = NO;
			requiredKeys = ( "type", "key" );
		};
		
		// "Root" variants of types which can have typeSpecifier arguments. These are the only types for which a $definitions entry is meaningful.
		$rootArrayTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "array" );
				};
				"$definitions" = $definitionsDictionary;
				
				"minCount" = positiveInteger;
				"maxCount" = positiveInteger;
				"valueType" = $typeSpecifier;
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$rootDictionaryTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "dictionary") ;
				};
				"$definitions" = $definitionsDictionary;
				
				"minCount" = positiveInteger;
				"maxCount" = positiveInteger;
				"valueType" = $typeSpecifier;
				"schema" =
				{
					type = dictionary;
					valueType = $typeSpecifier;
				};
				"allowOthers" = boolean;
				"requiredKeys" =
				{
					type = array;
					valueType = string;
				};
			};
			allowOthers = NO;
			requiredKeys = ( "type" );
		};
		$rootOneOfTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "oneOf" );
				};
				"$definitions" = $definitionsDictionary;
				
				"options" =
				{
					type = array;
					valueType = $typeSpecifier;
				};
			};
			allowOthers = NO;
			requiredKeys = ( "type", "options" );
		};
		$rootDelegatedTypeSpecifier =
		{
			type = dictionary;
			schema =
			{
				"type" =
				{
					type = enumeration;
					values = ( "delegatedType") ;
				};
				"$definitions" = $definitionsDictionary;
				
				"baseType" = $typeSpecifier;
				"key" = string;
			};
			allowOthers = NO;
			requiredKeys = ( "type", "key" );
		};
		$rootMacroInvocation =
		{
			type = oneOf;
			options =
			(
				$macroInvocationString,
				{
					type = dictionary;
					schema =
					{
						"type" = $macroInvocation;
						"$definitions" = $definitionsDictionary;
					};
					allowOthers = NO;
					requiredKeys = ( "type" );
				}
			);
		};
		
		
		$stringOrStringArray =
		{
			type = oneOf;
			options =
			(
				string,
				{
					type = array;
					valueType = string;
				}
			);
		};
		$filterSpecifier =
		{
			type = enumeration;
			filter = subStringToInclusive::;
			values =
			(
				"lowerCase",
				"upperCase",
				"capitalized",
				"truncFront:",
				"truncBack:",
				"subStringTo:",
				"subStringFrom:",
				"subStringToInclusive:",
				"subStringFromInclusive:"
			);
		};
		$definitionsDictionary =
		{
			// NOTE: there is currently no way to apply string requirements (like requiredPrefix = "$") to dictionary keys.
			type = dictionary;
			valueType = $typeSpecifier;
		};
	};
}
