Modul:Foundationclass

This is [[MediaWiki:Tagline]]. Set to <code>display:none</code> by chameleon skin.
Wechseln zu:Navigation, Suche
Documentation icon Module documentation[view] [edit] [history] [purge]

This is a Template for Class Modules. It implements a class in Lua using Module:Middleclass and provides serveral static and public methods, commonly used in the typical form>page>store process.

Usage

This is in a way an abstract class. It provides some static and public methods for resuse, and also maintains a store of private properties. If can be used to fast and easily create a typical class module that can handle:

  • creating an appropriate form
  • supplying your respective template with functionalities for
    • object initialization via template parameters, arguments, and datastore (self initialization)
    • general data assesment (basic plausibility tests)
    • data storage (supported are cargo and semantic media wiki)
    • error and warnings management
    • output and rendering
  • creating a template documentation

You can and should adjust the module in this methods:

  • individual attribute adjustment for the semantic forms fields
  • individual plausibility testing
  • individual data initialization
  • individual data store adjustments before stashing

You can access the objects core data store with getCoreData() and its uid with getUid().

How-To

This is, what you have to do (see Useful templates for content of the corresponding pages):

  1. create your base module Module:Name
  2. create your class module Module:Name/class. For this, copy lua sample code below this list and apply the following changes:
    1. change all occurences of className into your class name
    2. change the highlighted lines and replace 'Name' with the Name of your module
    3. implement the abstract methods:
      1. myAgumentProcessing(coreData)
      2. myDataAdjustments(data)
      3. myPlausibilityTest(args)
      4. myStashAdjustments(stash)
      5. static:mySfDynamicFieldAttribute(fieldname, attribute, value)
    4. for the usual output generation, implement (shells given in sample code)
      1. addInfobox()
      2. addPageBody()
  3. create and fill your configuration Module:Name/config
  4. create your template Template:Name
  5. create your form Form:Name
  6. if applicable, create your category page Category:Name
  7. fill in documentation:
    1. of your base module (place one invoke below the first comment and before the includeonly)
    2. of your class module (new docu for your own public, static and private methods (which you can probably copy from other classes) and one invoke to show the inheritance. Sample code provided in Useful templates
    3. of your class module's configuration (just copy content)
    4. of your template (one invoke below the first comment and before the includeonly)

Useful templates

private, public, class or instance

Zugriffsmatrix
- public private
instance
function MyClass:publicMethod()
MyClass.publicProperty
local _privateMethod = function (self, var)
_private[self]._privateProperty
class
function MyClass.static:staticMethod()
MyClass.static.staticProperty
-- accessing
MyClass.staticProperty
-- being "outside":
MyClass:staticMethod()
-- being inside a static method
self:staticMethod()
-- being inside a public method
self.class:staticMethod()
local _privateMethod = function (self, var)
local _privateProperty

private

property

you can declare a property private in one of two ways:

  1. anywhere, preferably before your constructor, declare a local var to have a private propery, this is not recommended, but it allows you to declare a private property, that can be used by your static methods. thus being somewhat private static
  2. for your instances, you can use the table _private[self] as a private store. it is available after your construrctor has been called (since this is an instance attribute)
method

a private method is declared thus: local _privateMethod = function(self, var). If you want to use the method in an public/static/other private method before it is declared (aka: the calling method is on a lower line number than your private method's declaration), decare it ahead and define it later:

local _privateImportantMethod
..
privateImportantMethod = function(self, var)

private class and instance methods are basically the same.

public

property

can be dclared via class.property. DONT DO THIS. It is bad style!

method

Declare them class:publicMethod(var). They are, after all, public

NOTE: public methods can be called by an instance, but also as static method:

function Class:public()
	return 'public: ' .. tostring(self)
end

local me = Class:new()
me.public()	-- returns "public: Instance of class Class"
Class:public()	-- returns "public: class Class"


static

property

Declare a static property like this: class.static.myProperty = 'Hello World'. This produces a public property, that can be accessed by the class, childclasses or instances via *.myProperty (replace * with class, object, childclass, ...)

method

Much the same as static properties, but you should use the colon operator: function Class.static:staticMethod() when definig the method. On accessig this method, you should use the dot-notation: function Class.staticMethod(self)

NOTE: Other than public methods, they can only be called by the class, but not by an instance:

function Class.static:static()
	return 'static: ' .. tostring(self)
end

local me = Class:new()
me.static()	-- returns "error:  attempt to call method 'static' (a nil value)"
Class:static()	-- returns "static: class Class"
Class.static(self)	-- this is not called via colon (:) operator as to preserve the childClass's scope when inheriting from this class


Methods

Constructor

new(uid, superhandler)

Creates a new Object for the class.

uid
variable, suggested
used to identify the object. defaults to mw.title.getCurrentTitle().prefixedText
superhandler
object of childclass of Foundationclass, optional
supply the superhandler, if you instanciate an object within another object. the instanciated object will pass errors and warnings to the superhandler and forgo adding an object category
return
object, of the class

private methods

These private Methods can not be used by child classes!

_amIPlausible(self)

Returs true or false, whether the object is plausible or not (i.e. has no errors or 1+)

self
object, me
return
boolean, whether the object is plausible or not

_debug(self, level, text)

Adds output to the internal debug log.

self
object, me
level
int; mandatory
debug level for the message
text
string; mandatory
debug message text
return
void

_plausibilityTest(self, args)

Checks, if input provided via template is plausible enough, to represent the object (to be stored and displayed). Also fills the list of errors and does some argument preparation.

self
object, me
args
table, mandatory
arguments passed to the template to be checked
return
boolean, whether the object is plausible or not

Properties

static

FoundationClass.globalConfig
table, holds some global configuration (data, that applies to all classes the same way). See Module:Foundationclass/globalconfig for details.
FoundationClass.myCargoUtil
table, instance of Module:CargoUtil
FoundationClass.mySwmUtil
table, instance of Module:SmwUtil
FoundationClass.myTableTools
table, instance of Module:TableTools
FoundationClass.myYesno
table, instance of Module:Yesno

private

Note: all private properties are stored in table _private[self]. _private is a static array that is indexed by self, so the table _private[self] holds all properties for instance self.

categories
table, holds the objects categories
coreData
table, holds the objects data ofter initializing
dbg
object, my instance of Module:Debug/class for debugging purposes
errors
table, a list of errors that occurred
fullpagename
string, name of currentpage including namespace
initialized
boolean, is this object properly initialized
loadedDataFromDataStore
boolean, did this object get its data from the data store
output
object of type mw.html, holds all output to be rendered()
pagename
string, name of current page (or current page's super page) w/o namespace
uid
string, uinique identifier ('NIL' if unset)
warnings
table, a list of warnings that came up

Configuration Data

This class holds gets its control data from the child class. Also, there is some global configuration data in Module:Foundationclass/globalconfig, that applies to all classes.

Dependencies

If you want to use Module:Foundationclass, you also need the following items (and their doc pages!):

also

and of course Extension:Semantic Forms

Also, have a look at the extended functionality of the Class engine.

Interface messages

To generate the Useful_templates, Foundationclass makes use of the following interface messages:

Next in dev queue

  1. have foundation class try to detect integer inputs (by td_type or cargo_type) and run a regexp in _plausibilityTest
  2. have foundationclass use mw message system for printouts instead of hardcoded text
  3. a template or page, that #autogenerates all categories, defined in Module:Foundationclass/globalconfig

local Class = require('Module:Middleclass').class
local FoundationClass = Class('FoundationClass')
local ClassDebug = require('Module:Debug/class')
local ClassSFfield = require('Module:SFfield/class')

-- ****************************************************************
-- *                          properties                          *
-- ****************************************************************

-- **************** initialization of table for private properties
local _private = setmetatable({}, {__mode = "k"})   -- weak table, storing all private attributes

-- **************** declaration of static properties
FoundationClass.static.globalConfig = mw.loadData( 'Module:Foundationclass/globalconfig' )
FoundationClass.static.myCargoUtil = require('Module:CargoUtil')
FoundationClass.static.mySmwUtil = require('Module:SmwUtil')
FoundationClass.static.myTableTools = require('Module:TableTools')
FoundationClass.static.myYesno = require('Module:Yesno')
-- these are later set by my child class:
local _debug	-- function for debugging, declared later

-- ***************************************************************
-- *                           methods                           *
-- ***************************************************************

-- **************** declaration of static methods
function FoundationClass:initialize(user_uid, superHandler)	-- constructor
	local user_uid = user_uid or mw.title.getCurrentTitle().prefixedText
	local shs = superHandler
	local superHandler = (superHandler and type(superHandler) == 'table' and superHandler.addError and type(superHandler.addError) == 'function') and superHandler or nil
	local _CFG = self.class.myConfiguration
	local idString = user_uid and ('myUid_' ..user_uid)  or ('NIL_' .. mw.title.getCurrentTitle().prefixedText)
	-- initialize all private properties
	_private[self] = {
		categories = {},
		coreData = {},
		dbg = ClassDebug:new('class FoundationClass: ' .. idString),
		errors = {},
		initialized = false,
		loadedDataFromDataStore = false,
		loadedDataMyself = false,
		output = mw.html.create(''),
		superHandler = superHandler,
		uid = user_uid,
		warnings = {},
	}
	if _CFG.global.category and mw.title.getCurrentTitle().namespace ~= 10 then
		self:addCategory(_CFG.global.category)
	end
	_debug(self, 1, 'Initializing object "' .. idString ..'", from ' .. tostring(self))
	_debug(self, 1, ' my superhandler was ' .. tostring(shs))
	_debug(self, 1, ' my superhandler is ' .. tostring(superHandler))
end

function FoundationClass.static:cargoGenerateTableStructure()
	_debug(self, 1, 'entering FoundationClass.static:cargoGenerateTableStructure(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	-- warning: calling FoundationClass:cargoGenerateTableStructure() gives us no access to our configuration, use Class.cargoGenerateTableStructure(self) instead
	local declaration = {}
	-- first, add the uid field
	declaration[FoundationClass.globalConfig.uidFieldName] = 'Text (hidden)'
	for param, paramData in pairs(_CFG.parameter) do
		if paramData.cargo_type and mw.ustring.len(paramData.cargo_type) > 0 then
			if paramData.cardinality and paramData.cardinality == 'list' then
				declaration[param] = 'List (' .. (_CFG.parameter[param].sf and _CFG.parameter[param].sf.delimiter or _CFG.global.delimiter) .. ') of ' .. paramData.cargo_type
			else
				declaration[param] = paramData.cargo_type
			end
			local additional_field_parameters = {}
			if paramData.cargo_hidden then
				table.insert(additional_field_parameters, 'hidden')
			end
			if paramData.cargo_size and (type(paramData.cargo_size) == 'number' or mw.ustring.match(paramData.cargo_size, '^[0-9]+$')) and
					FoundationClass.myTableTools.inTable(FoundationClass.globalConfig.cargoTypesElegibleForSize, mw.ustring.lower(paramData.cargo_type)) then
				table.insert(additional_field_parameters, 'size=' .. tostring(paramData.cargo_size))
			end
			if paramData.values and FoundationClass.myTableTools.size(paramData.values) > 0 and
					FoundationClass.myTableTools.inTable(FoundationClass.globalConfig.cargoTypesElegibleForValue, mw.ustring.lower(paramData.cargo_type)) then
				local av = 'allowed values='
				for _, v in pairs(paramData.values) do
					av = av .. v .. ','
				end
				table.insert(additional_field_parameters, mw.ustring.sub(av, 1, -2))
			end
			if #additional_field_parameters > 0 then
				declaration[param] = declaration[param] .. ' (' .. table.concat(additional_field_parameters, ';') .. ')'
			end
		end
	end
	return declaration
end

function FoundationClass.static:categorize()
	local thispage = mw.title.getCurrentTitle()
	local _CFG = self.myConfiguration
	local ret = ''
	if self and _private[self] and _private[self].uid then
		error('FoundationClass:categorize() must be called statically. Furthermore, it is not meant for categorizing class\'s objects but rather all pages belonging to the class\'s data pipeline (form, categories, modules, template, ...)')
	elseif thispage.rootText == 'Classgenerator' or thispage.rootText == 'Foundationclass' then
		ret = ret .. '[[Category:Class engine]]'
		if FoundationClass.globalConfig.classCategory and mw.ustring.len(FoundationClass.globalConfig.classCategory) > 0 then
			-- there has been a classCategory configured
			if thispage.text == 'Foundationclass' or (thispage.subpageText and thispage.subpageText == 'class')  then
				-- add class category if on a class page or on foundationclass
				ret = ret .. '[[Category:' .. FoundationClass.globalConfig.classCategory .. ']]'
			end
			if thispage.subpageText and (thispage.subpageText == 'config' or thispage.subpageText == 'globalconfig') then
				-- add class category if on a config page
				local basepage = mw.ustring.match(thispage.fullText, '^[^:]*:(.+)/config.*$')
				local classpage
				if basepage then
					classpage = basepage .. '/class'
				else
					classpage = thispage.rootText .. '/config'
				end
				ret = ret .. '[[Category:' .. FoundationClass.globalConfig.classCategory .. '|' .. classpage .. ']]'
			end
		end
	elseif thispage.namespace == 10 then
		-- template namespace: add category if in correct namespace and there has been a templateCategory configured, also make sure, we are not part of classgenerator
		if FoundationClass.globalConfig.templateCategory and mw.ustring.len(FoundationClass.globalConfig.templateCategory) > 0 then
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.templateCategory .. ']]'
		end
	elseif thispage.namespace == 14 then
		-- category namespace
		if _CFG and _CFG.global and _CFG.global.gardeningCategory == thispage.text and FoundationClass.globalConfig.gardeningSuperCategory and mw.ustring.len(FoundationClass.globalConfig.gardeningSuperCategory) and thispage.text ~= FoundationClass.globalConfig.gardeningSuperCategory then
			-- if class' gardening category and not the supergardening itself -> set gardening supercategory
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.gardeningSuperCategory .. ']]'
		elseif FoundationClass.globalConfig.projectSuperCategory and mw.ustring.len(FoundationClass.globalConfig.projectSuperCategory) > 0 then
			-- not a gardening category and not called by a childclass. projectSuperCategory set
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.projectSuperCategory .. ']]'
		end
	elseif thispage.namespace == mw.title.makeTitle('Form', 'NUL').namespace then
		-- form namespace: add category if in correct namespace and there has been a formCategory configured, also make sure, we are not part of classgenerator
		if FoundationClass.globalConfig.formCategory and #FoundationClass.globalConfig.formCategory > 0 then
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.formCategory .. ']]'
		end
	elseif thispage.namespace == mw.title.makeTitle('Module', 'NUL').namespace then
		-- module namespace: add category if in correct namespace and there has been a moduleCategory configured, also make sure, we are not part of classgenerator or foundationclass
		if FoundationClass.globalConfig.moduleCategory and #FoundationClass.globalConfig.moduleCategory > 0 then
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.moduleCategory .. ']]'
		end
		if thispage.subpageText and thispage.subpageText == 'class' and FoundationClass.globalConfig.classCategory and mw.ustring.len(FoundationClass.globalConfig.classCategory) > 0 then
			-- add class category if on a class page and there has been a classCategory configured
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.classCategory .. ']]'
		end
		if thispage.subpageText and thispage.subpageText == 'config' and FoundationClass.globalConfig.classCategory and mw.ustring.len(FoundationClass.globalConfig.classCategory) > 0 then
			-- add class category if on a config page and there has been a classCategory configured
			local basepage = mw.ustring.match(thispage.fullText, '^[^:]*:(.+)/config.*$')
			local classpage
			if basepage then
				classpage = basepage .. '/class'
			else
				classpage = thispage.rootText .. '/config'
			end
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.classCategory .. '|' .. classpage .. ']]'
		end
	elseif thispage.namespace == mw.title.makeTitle('Property', 'NUL').namespace then
		-- property namespace: add category if in correct namespace and there has been a propertyCategory configured
		if FoundationClass.globalConfig.propertyCategory and #FoundationClass.globalConfig.propertyCategory > 0 then
			ret = ret .. '[[Category:' .. FoundationClass.globalConfig.propertyCategory .. ']]'
		end
	end
	return ret
end

function FoundationClass.static:categoryPage()
	_debug(self, 1, 'entering FoundationClass.static:categoryPage(), from ' .. tostring(self))
	local html = mw.html.create('')
	if mw.title.getCurrentTitle().namespace == 14 then
		local frame = mw.getCurrentFrame()
		local _CFG = self.myConfiguration
		local form
		if _CFG.form.enable and _CFG.form.name and mw.ustring.len(_CFG.form.name) then
			form = frame:callParserFunction('#default_form:' .. _CFG.form.name)
		else
			form = 'Seiten in dieser Kategorie haben kein Standardformular'
		end
		local mbox = require('Module:Message box').main('cmbox', 
			{
				type = 'notice',
				text = _CFG.global.description .. '\n* ' .. form .. '\n* ' .. 
						'Seiten in  dieser Kategorie nutzen die Vorlage [[Template:' .. _CFG.template.name .. ']]'
			})
		html:wikitext(tostring(mbox))
			:node(self:explainDataStore())	-- remember: self refers to a class, not an object and this method is static
	else
		error('FoundationClass:categoryPage() must be called from namespace category!', 2)
	end
	return html
end

function FoundationClass.static:explainDataStore()
	_debug(self, 1, 'entering FoundationClass.static:explainDataStore(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local collapse = require('Module:Collapse').main
	local str = ''
	if FoundationClass.usesDataStore(self, 'cargo') then
		local declaration = self:cargoGenerateTableStructure()	-- remember: self refers to a class, not an object and this method is static
		-- add the default fields
		for field, description in pairs(FoundationClass.globalConfig.cargoSpecialFields) do
			declaration[field] = description
		end
		local fields = {}
		for key, _ in pairs(declaration) do
			table.insert(fields, key)
		end
		table.sort(fields)
		local headline = 'Declaration for CARGO table "' .. (_CFG.global.cargoTable or 'NIL') .. '"'
		local content = '<ul>'
		for _, field in pairs(fields) do
			local text = declaration[field]
			if _CFG.parameter[field] and _CFG.parameter[field].description then
				text = text .. ', <small>' .. _CFG.parameter[field].description .. '</small>'
			elseif field == FoundationClass.globalConfig.uidFieldName then
				text = text .. ', <small>Automatisch hinzugefügt! Wird verwendet, um individuelle Objekte innerhalb der Klasse zu identifizieren.</small>'
			end
			content = content .. '<li> ' .. field .. ': ' .. text .. '</li>'
		end
		content = content .. '</ul>\n[[Spezial:CargoTables/'.. (_CFG.global.cargoTable or 'Instance') .. '|View Data]]'
		str = str ..  collapse{header = headline, content = content, left = true} .. '\n'
	end
	if FoundationClass.usesDataStore(self, 'smw') then	-- smw store
		local property2param = FoundationClass.smwGetProperty2ParameterTranslationTable(self)
		local properties = {}
		local lang = mw.language.getContentLanguage()
		for prop, field in pairs(property2param) do
			if FoundationClass.globalConfig.ucfirst then
				property = lang:ucfirst(prop)
			else
				property = prop
			end
			properties[property] = {
				individual = true, -- essentially says: no special property
			}
			if _CFG.parameter[field] then
				properties[property].description = _CFG.parameter[field].description
				properties[property].type = (_CFG.parameter[field].property_type and _CFG.parameter[field].property_type or 'Page')
				properties[property].values = (_CFG.parameter[field].values and #_CFG.parameter[field].values > 0) and ' (allowed values=' .. table.concat(_CFG.parameter[field].values, ', ') .. ')' or ''
			elseif field == FoundationClass.globalConfig.smwClassProperty then
				properties[property].description = 'Automatisch hinzugefügt! Wird verwendet, um Mitglieder dieser Klasse zu finden (funktioniert auch, ohne dass eine Kategorie definiert wird und auch fur subobjects)'
				properties[property].type = 'Text'
				properties[property].values = ''
			elseif field == FoundationClass.globalConfig.uidFieldName then
				properties[property].description = 'Automatisch hinzugefügt! Wird verwendet, um individuelle Objekte innerhalb der Klasse zu identifizieren.'
				properties[property].type = 'Text'
				properties[property].values = ''
			else
				-- should never apply
				properties[property].description = 'THIS should not have appeared. Please consult an admin'
				properties[property].type = 'WARNING!'
				properties[property].values = ' (see Module:Foundationclass, FoundationClass.static:explainDataStore() for more information)'
			end
			properties[property].text = ((_CFG.parameter[field] and _CFG.parameter[field].cardinality == 'list') and 'List of ' or '') .. properties[property].type .. properties[property].values ..
				', <small>' .. properties[property].description .. '</small>'
		end
		if _CFG and _CFG.global and not _CFG.global.smwIsSubobject then
			for key, value in pairs(FoundationClass.globalConfig.smwSpecialProperties) do
				if FoundationClass.globalConfig.ucfirst then
					property = lang:ucfirst(key)
				else
					property = key
				end
				properties[property] = { text = value, ucname }
			end
		end
		local propertyList = {}
		for key, _ in pairs(properties) do
			table.insert(propertyList, key)
		end
		table.sort(propertyList)
		local headline = 'Semantic properties used in this class:'
		local content = '<ul>'
		for _, property in pairs(propertyList) do
			content = content .. '<li>[[Property:' .. property .. ']]: ' .. properties[property].text .. '</li>'
		end
		content = content .. '</ul>\n'
		local attributes = {headers='plain', format='table', limit=0, searchlabel='Hier klicken für eine tabellarische Übersicht!'}
		local fields = {}
		for key, _ in pairs(properties) do
			table.insert(fields, key)
		end
		table.sort(fields)
		content = content .. FoundationClass.mySmwUtil.rawask({where='[[' .. FoundationClass.globalConfig.smwClassProperty .. '::' .. self.name .. ']]', fields = fields }, attributes) .. '\n'
		str = str .. collapse{header = headline, content = content, left = true} .. '\n'
	end
	if #mw.text.trim(str) == 0 then
		str = 'This template does not store any persistent data.'
	end
	str = '\n== Data Storage ==\n' .. str
	return str
end

function FoundationClass.static:formRedlink(target, form, linktext, queryString)
	_debug(self, 1, 'entering FoundationClass.static:formRedlink() to create a redlink to a form')
	local target = target
	local form = form
	local linktext = linktext or target
	local str
	if target and form then
		local frame = mw.getCurrentFrame()
		args = { form = form }
		if linktext then
			args['link text'] = linktext
			args['existing page link text'] = linktext
		end
		if queryString then
			args['query string'] = queryString
		end
		str = frame:callParserFunction{ name='#formredlink:target=' .. target, args=args }
	else
		str = 'target or form missing on call to _formredlink()'
	end
	return str
end

function FoundationClass.static:gardeningCategoryPage()
	_debug(self, 1, 'entering FoundationClass.static:gardeningCategoryPage(), from ' .. tostring(self))
	local html = mw.html.create('')
	if mw.title.getCurrentTitle().namespace == 14 then
		local frame = mw.getCurrentFrame()
		local _CFG = self.myConfiguration
		local mbox = require('Module:Message box').main
		html:wikitext(tostring(mbox('cmbox', 
			{
				type = 'notice',
				text = 'This category shows all elements of class ' .. self.name .. ' that have at least one error in their core data. This category must be empty at all times!'
			})))
			:newline()
			:wikitext(tostring(mbox('cmbox', 
			{
				type = 'content',
				text = 'This category should be empty! Please tend to the articles contained herein!'
			})))
		-- add category if in namespace category and there has been a supercategory configured
		if FoundationClass.globalConfig.gardeningCategory and #FoundationClass.globalConfig.gardeningCategory > 0 and FoundationClass.globalConfig.gardeningCategory ~= mw.title.getCurrentTitle().text then
			html:wikitext('[[Category:' .. FoundationClass.globalConfig.gardeningCategory .. ']]')
		end
	else
		error('FoundationClass:gardeningCategoryPage() must be called from namespace category!', 2)
	end
	return html
end

function FoundationClass.static:mwLink(target)
	-- just a util method
	if not target then
		return ''
	elseif type(target) == 'table' then
		return '[[' .. mw.text.listToText(target, ']], [[', ']] und [[') .. ']]'
	else
		return '[[' .. target .. ']]'
	end
end

function FoundationClass.static:sfGenerateForm()
	_debug(self, 1, 'entering FoundationClass.static:sfGenerateForm(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	if not _CFG.form.enable then
		return require('Module:Error').error{ message = 'Error: This class has no form page. Could not create link to form!', type = 'div' }
	end
	local tagStart = '{{{'
	local tagEnd = '}}}'
	-- create the form header
	local notification = mw.html.create('')
	if _CFG.form.notification and mw.ustring.len(_CFG.form.notification) > 0 then
		local mbox = require('Module:Message box')
		notification:wikitext(mbox.main('ombox', { text = _CFG.form.notification }))
			:newline()
	end
	local formheader = mw.html.create('')
	formheader:wikitext(self:sfGenerateFormInfoTag())
		:addClass('description-box')
		:wikitext('<h2>' .. _CFG.global.entityTitle .. '</h2>')
		:tag('p')
			:wikitext(_CFG.form.headerText)
			:done()
			:newline()
		:node(notification)
		:done()
		:tag('div')
			:attr('id', 'wikiPreview')
			:cssText('display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA')
			:done()
			:newline()
	-- deal with sections and free text
	local sections = mw.html.create('')
	local sectionlist = _CFG.form.sectionList and _CFG.form.sectionList or {}
	for _, section in pairs(sectionlist) do
		sections:wikitext('<h2>' .. section .. '</h2>')
			:newline()
			:wikitext(tagStart .. 'section|' .. section .. '|level=2|autogrow=true|cols=' .. _CFG.form.textareaAttributes.cols .. 
							'|rows=' .. math.max(8, 2*_CFG.form.textareaAttributes.rows) .. tagEnd)
			:newline()
	end
	local freetext = mw.html.create('')
	if _CFG.form.allowsFreeText then
		freetext:wikitext(tagStart .. 'standard input|free text|autogrow=true|editor=wikieditor|cols=' .. math.min(200, 2*_CFG.form.textareaAttributes.cols) .. 
							'|rows=' .. math.min(48, 5*_CFG.form.textareaAttributes.rows) .. tagEnd)
			:newline()
	end
	-- create the form buttons
	local formbuttons = mw.html.create('div')	-- we have to use a div here. this somehow helps the includes/SF_FormPrinter.php to correcly place the fieldset (blue box) around the form
	if FoundationClass.myTableTools.inTable(_CFG.form.buttons, 'summary') then
		formbuttons:tag('p')
			:wikitext(tagStart .. 'standard input|summary' .. tagEnd)
			:done()
			:newline()
	end
	for _, button in pairs(_CFG.form.buttons) do
		if button ~= 'summary' then
			formbuttons:wikitext(tagStart .. 'standard input|' .. button .. tagEnd)
		end
	end
	formbuttons:addClass('formbuttons')
		:done()
	-- assemble everything, so start the html object
	local html = mw.html.create('div')
	html:node(formheader)
		:wikitext(tagStart .. 'for template|' .. _CFG.template.name .. '|label=' .. _CFG.global.entityTitle .. tagEnd)
		:newline()
		:node(self:sfGenerateFormTable())
		:newline()
		:wikitext("'''(*) Pflichtfeld'''<br>")
		:newline()
		:wikitext(tagStart .. 'end template' .. tagEnd)
		:newline()
		:node(sections)
		:node(freetext)
		:node(formbuttons)
	local debugLevel = FoundationClass.globalConfig.debugLevel or self.myConfiguration.global.debugLevel
	if debugLevel then
		html:newline()
			:newline()
			:wikitext('<h2>Debug level is ' .. debugLevel .. '</h2>')
			:newline()
			:wikitext(ClassDebug:printLog(debugLevel))
	end
	local getArgs = require('Module:Arguments').getArgs
	local frame = mw.getCurrentFrame()
	local args = getArgs(frame)
	if args.dump == "true" then
		return mw.text.nowiki( tostring(html) )
	end
	return html
end

function FoundationClass.static:sfGenerateFormEntry()
	_debug(self, 1, 'entering FoundationClass.static:sfGenerateFormEntry(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local html = mw.html.create('')
	local thispage = mw.title.getCurrentTitle()
	html:wikitext('<h2>' .. _CFG.global.entityTitle .. '</h2>')
		:newline()
		:tag('p')
			:wikitext(_CFG.form.teaserText)
			:done()
		:newline()
	if _CFG.form.typeCreateLink and (mw.ustring.lower(_CFG.form.typeCreateLink) == 'forminput' or mw.ustring.lower(_CFG.form.typeCreateLink) == 'formlink') then
		html:tag('p')
			:wikitext(_CFG.form.createInfotext)
			:wikitext(self:sfGenerateFormLink())	-- remember: self refers to a class, not an object and this method is static
			:done()
			:newline()
	end
	local getArgs = require('Module:Arguments').getArgs
	local frame = mw.getCurrentFrame()
	local args = getArgs(frame)
	if args.dump == "true" then
		return mw.text.nowiki( tostring(html) )
	end
	return html
end

function FoundationClass.static:sfGenerateFormInfoTag()
	_debug(self, 1, 'entering FoundationClass.static:sfGenerateFormInfoTag(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local frame = mw.getCurrentFrame()
	if not _CFG.form.enable then
		return require('Module:Error').error{ message = 'Error: This class has no form page. Could not create link to form!', type = 'div' }
	end
	local tagStart = '{{{'
	local tagEnd = '}}}'
	local queryString = ''
	if (not _CFG.form.typeCreateLink or mw.ustring.lower(_CFG.form.typeCreateLink) ~= 'forminput') and _CFG.form.createLinkPageName and #_CFG.form.createLinkPageName > 0 then
		local pageName = _CFG.form.createLinkPageName
		if _CFG.global.namespace and mw.ustring.len(_CFG.global.namespace) > 0 and not mw.ustring.match(pageName, '^' .. _CFG.global.namespace .. ':.+$') then
			pageName = _CFG.global.namespace .. ':' .. pageName
		end
		queryString = '|page name=' .. pageName
	end
	return tagStart .. 'info' .. queryString .. '|add title=' .. _CFG.form.labelCreate .. '|edit title=' .. _CFG.form.labelEdit .. tagEnd
end

function FoundationClass.static:sfGenerateFormLink()
	_debug(self, 1, 'entering FoundationClass.static:sfGenerateFormInput(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	if not _CFG.form.enable or not _CFG.form.name or #_CFG.form.name == 0 then
		return require('Module:Error').error{ message = 'Error: This class has no form page. Could not create link to form!', type = 'div' }
	end
	local frame = mw.getCurrentFrame()
	local ret = ''
	if _CFG.form.typeCreateLink and (_CFG.form.typeCreateLink == 'forminput' or _CFG.form.typeCreateLink == 'formlink') then
		if _CFG.form.typeCreateLink == 'forminput' then
			local args = { size = 40 }
			if _CFG.form.createInputPlaceholder and mw.ustring.len(_CFG.form.createInputPlaceholder) > 0 then
				args.placeholder = _CFG.form.createInputPlaceholder
			end
			if _CFG.form.InputQueryString and mw.ustring.len(_CFG.form.InputQueryString) > 0 then
				args['query string'] = _CFG.form.InputQueryString
			end
			args['button text'] = _CFG.form.labelCreate
			if _CFG.global.namespace and mw.ustring.len(_CFG.global.namespace) > 0 then
				if args['query string'] then
					args['query string'] = args['query string'] .. '&' .. 'namespace=' .. _CFG.global.namespace
				else
					args['query string'] = 'namespace=' .. _CFG.global.namespace
				end
			end
			ret = frame:callParserFunction{name = '#forminput:form=' .. _CFG.form.name, args = args}
		else
			local args = {
				tooltip = _CFG.form.createInfotext
			}
			if _CFG.form.createLinkQueryString and mw.ustring.len(_CFG.form.createLinkQueryString) > 0 then
				args['query string'] = _CFG.form.createLinkQueryString
			end
			local linkType = _CFG.form.createLinkType
			if linkType then
				linkType = mw.ustring.lower(linkType)
				if linkType ~= 'button' and linkType ~= 'post button' then
					linkType = nil
				end
			end
			args['link text'] = _CFG.form.labelCreate
			args['link type'] = linkType
			ret = frame:callParserFunction{name = '#formlink:form=' .. _CFG.form.name, args = args}
		end
	end
	return ret
end

function FoundationClass.static:sfGenerateFormTable(fieldlist, tableid)
	_debug(self, 1, 'entering FoundationClass.static:sfGenerateFormTable(fieldlist), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local fieldlist = fieldlist or _CFG.form.fieldOrder
	-- set some form defaults
	ClassSFfield:setDefaultSize(_CFG.form.fieldSize)
	ClassSFfield:setDefaultTextAreaCols(_CFG.form.textareaAttributes.cols)
	ClassSFfield:setDefaultTextAreaEditor(_CFG.form.textareaAttributes.editor)
	ClassSFfield:setDefaultTextAreaRows(_CFG.form.textareaAttributes.rows)
	-- now create the form table
	local hidden = mw.html.create('')
	local formtable = mw.html.create('table')
	for _, fieldname in pairs(fieldlist) do
		if _CFG.parameter[fieldname] and not self:mySfDynamicFieldAttribute(fieldname, 'disable', false)  then
			local field = self:sfInitField(fieldname)
			if field then
				if field:get('hidden') then
					hidden:wikitext(field:render())
				else
					formtable:node(field:createTr(_CFG.parameter[fieldname].label, _CFG.parameter[fieldname].description))
				end
			elseif _CFG.parameter[fieldname].label and _CFG.parameter[fieldname].description and (not _CFG.parameter[fieldname].td_type or mw.ustring.len(_CFG.parameter[fieldname].td_type) == 0) then
				mbox = require('Module:Message box').main('cmbox', { type = 'notice', text = "'''" .. _CFG.parameter[fieldname].label .. ":''' " .._CFG.parameter[fieldname].description})
				formtable:tag('tr')
					:attr('id', ClassSFfield:getShowOnSelectIdPrefix() .. mw.uri.encode(fieldname, 'WIKI'))
					:tag('td')
						:attr('colspan', 2)
						:wikitext(tostring(mbox))
						:done()
			end
		end
	end
	formtable:addClass(FoundationClass.globalConfig.formTableClass)
		:css('width', 'auto')
	if tableid then
		formtable:attr('id', tableid)
	end
	formtable:done()
		:node(hidden)
	return formtable
end

function FoundationClass.static:sfInitField(fieldname)
	_debug(self, 1, 'entering FoundationClass.static:sfInitField(fieldname) to initialize field ' .. fieldname or 'NOT SET' .. ', from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local field
	if _CFG.parameter[fieldname] and _CFG.parameter[fieldname].sf and not self:mySfDynamicFieldAttribute(fieldname, 'disable', false) then
		field = ClassSFfield:new(fieldname)
		if _CFG.parameter[fieldname].cardinality and _CFG.parameter[fieldname].cardinality == 'list' then
			field:set('list', self:mySfDynamicFieldAttribute(fieldname, 'list', true))
			local delimiter = _CFG.parameter[fieldname].sf and _CFG.parameter[fieldname].sf.delimiter or _CFG.global.delimiter
			-- if we have a global or local delimiter set which is not the comma (','), provide it here automatically
			if delimiter ~= ',' then
				field:set('delimiter', self:mySfDynamicFieldAttribute(fieldname, 'delimiter', delimiter))
			end
		end
		if _CFG.parameter[fieldname].severity and _CFG.parameter[fieldname].severity == 'mandatory' then
			field:set('mandatory', self:mySfDynamicFieldAttribute(fieldname, 'mandatory', true))
		end
		if _CFG.parameter[fieldname].values then
			field:set('values', self:mySfDynamicFieldAttribute(fieldname, 'values', _CFG.parameter[fieldname].values))
		end
		if _CFG.parameter[fieldname].sf then
			for attr, val in pairs(_CFG.parameter[fieldname].sf) do
				field:set(mw.ustring.gsub(attr, '_', ' '),
						self:mySfDynamicFieldAttribute(fieldname, attr, val)	-- remember: self refers to a class, not an object and this method is static
					)
			end
		end
	end
	return field
end

function FoundationClass.static:smwGetProperty2ParameterTranslationTable()
	_debug(self, 1, 'entering FoundationClass.static:smwGetProperty2ParameterTranslationTable(), from ' .. tostring(self))
	if not FoundationClass.usesDataStore(self, 'smw') then
		return nil
	end
	if not self.smwProperty2ParameterTranslationTable or FoundationClass.myTableTools.size(self.smwProperty2ParameterTranslationTable) == 0 then
		local _CFG = self.myConfiguration
		local translation = {}
		translation[mw.ustring.gsub(FoundationClass.globalConfig.smwClassProperty, ' ', '_')] = mw.ustring.gsub(FoundationClass.globalConfig.smwClassProperty, ' ', '_')
		translation[mw.ustring.gsub(FoundationClass.globalConfig.uidFieldName, ' ', '_')] = mw.ustring.gsub(FoundationClass.globalConfig.uidFieldName, ' ', '_')
		for param, data in pairs(_CFG.parameter) do
			if data.property_name and data.property_name:len() > 0 then
				translation[mw.ustring.gsub(data.property_name, ' ', '_')] = param
			end
		end
		self.smwProperty2ParameterTranslationTable = translation
	end
	return self.smwProperty2ParameterTranslationTable
end

function FoundationClass.static:templateDocumentation(arg)
	_debug(self, 1, 'entering FoundationClass.static:templateDocumentation(), from ' .. tostring(self))
	local _CFG = self.myConfiguration
	local frame = mw.getCurrentFrame()
	local arg = arg
	local debugLevel = FoundationClass.globalConfig.debugLevel or self.myConfiguration.global.debugLevel
	local thispage = mw.title.getCurrentTitle()
	local luaBanner = require('Module:Lua banner')
	-- first generate the template data for the "parameters"-section
	local parameter = {}
	for _, param in pairs(_CFG.form.fieldOrder) do
		if _CFG.parameter[param] and _CFG.parameter[param].td_type then
			table.insert(parameter, param)
		end
	end
	local templateData = ''
	if #parameter > 0 then
		local globalDescription = mw.ustring.gsub(mw.ustring.gsub(_CFG.global.description, '\t', ' '), '\n', ' ')
		local jsonData = '{\n\t"description": "' .. globalDescription .. '",\n' ..
			'\t"params": {\n'
		for _, param in pairs(parameter) do
			paramdata = _CFG.parameter[param]
			jsonData = jsonData .. '\t\t"' .. param .. '": {\n'
			jsonData = jsonData .. '\t\t\t"type": "' .. mw.ustring.lower(paramdata.td_type) .. '",\n'
			if paramdata.td_default then
				jsonData = jsonData .. '\t\t\t"default": "' .. paramdata.td_default .. '",\n'
			end
			if paramdata.description then
				--jsonData = jsonData .. '\t\t\t"description": "' .. mw.ustring.gsub(paramdata.description, '"', '\'\'') .. '",\n'
				--since 1.31 \n and \t are not allowed in json string, so replace them with a space
				local paramDescription = mw.ustring.gsub(paramdata.description, '"', '\'\'')
				paramDescription = mw.ustring.gsub(mw.ustring.gsub(paramDescription, '\t', ' '), '\n', ' ')
				jsonData = jsonData .. '\t\t\t"description": "' .. paramDescription .. '",\n'
			end
			if paramdata.label then
				jsonData = jsonData .. '\t\t\t"label": "' .. paramdata.label .. '",\n'
			end
			if paramdata.severity then
				jsonData = jsonData .. '\t\t\t"suggested": ' .. ((paramdata.severity == 'suggested' or paramdata.severity == 'optional') and 'true,\n' or 'false,\n')
				jsonData = jsonData .. '\t\t\t"required": ' .. ((paramdata.severity == 'mandatory' or paramdata.severity == 'required') and 'true,\n' or 'false,\n')
			end
			jsonData = mw.ustring.sub(jsonData, 1, -3) .. '\n\t\t},\n'
		end
		jsonData = mw.ustring.sub(jsonData, 1, -3) .. '\n\t}\n}'
		if debugLevel then
			templateData = '<pre>' .. jsonData .. '</pre>'
		else
			templateData = frame:extensionTag('templatedata', jsonData)
		end
	else
		templateData = 'Dieses Template verfügt über keine nutzbaren (statischen) parameter.'
	end
	-- then assemble the code for the usage section
	local code = '{{' .. thispage.rootText .. '\n'
	for _, param in pairs(parameter) do
		code = code .. '|' .. param .. '=\n'
	end
	code = code .. '}}\n'
	-- the 'see also' links
	local seeAlso = mw.html.create('')
	local seeAlsoList = mw.html.create('')
	if _CFG.template.templateDocumentationSeeAlso then
		if type(_CFG.template.templateDocumentationSeeAlso) == 'table' then
			for _, link in pairs(_CFG.template.templateDocumentationSeeAlso) do
				if mw.ustring.find(link, ':', 1, true) then
					link = mw.ustring.match(link, '^[^:]*:(.+)$')
				end
				seeAlsoList:tag('li')
					:wikitext('{{[[:Template:' .. link .. '|' .. link .. ']]}}')
					:done()
					:newline()
			end
		else
			local link = _CFG.global.templateDocumentationSeeAlso
			if mw.ustring.find(link, ':', 1, true) then
				link = mw.ustring.match(link, '^[^:]*:(.+)$')
			end
			seeAlsoList:tag('li')
				:wikitext('{{[[:Template:' .. link .. '|' .. link .. ']]}}')
				:done()
				:newline()
		end
		seeAlso:wikitext('<h2>See also</h2>')
			:newline()
			:tag('ul')
				:node(seeAlsoList)
				:done()
				:newline()
	end
	-- assemble
	local html = mw.html.create('')
	html:wikitext('<h2>Description</h2>')
		:newline()
		:wikitext(luaBanner._main({'Module:' .. thispage.rootText, 'Module:' .. thispage.rootText .. '/class',
					'Module:' .. thispage.rootText .. '/config', 'Module:Foundationclass', 'Module:Middleclass'}))
		:newline()
		:wikitext(_CFG.global.description .. ((_CFG.global.category and mw.ustring.len(_CFG.global.category) > 0) and ('\n\n' .. mw.site.namespaces[14].name .. ': [[:Category:' .. _CFG.global.category .. ']]\n') or ''))
		:newline()
		:node(self:explainDataStore())	-- remember: self refers to a class, not an object and this method is static
		:newline()
		:newline()
		:wikitext('<h2>Parameters</h2>')
		:wikitext(templateData)
		:newline()
		:newline()
		:wikitext('<h2>Usage</h2>')
		:wikitext(frame:expandTemplate{ title = 'Code', args = {'html4strict', code} })
		:newline()
		:node(seeAlso)
		:done()
	if debugLevel then
		html:newline()
			:newline()
			:wikitext('<h2>Debug level is ' .. debugLevel .. '</h2>')
			:newline()
			:wikitext(ClassDebug:printLog(debugLevel))
	end
	return html
end

function FoundationClass.static:usesDataStore(storeType)
	_debug(self, 1, 'entering FoundationClass.static:usesDataStore(storeType) to do assertain, if store "' .. storeType .. '" is used, from ' .. tostring(self))
	local _CFG = self.myConfiguration
	return (FoundationClass.globalConfig.dataStore == 'both' or FoundationClass.globalConfig.dataStore == mw.ustring.lower(storeType)) and 
		(mw.ustring.lower(storeType) == 'smw' and _CFG.global.smwUseStorage) or (mw.ustring.lower(storeType) == 'cargo' and _CFG.global.cargoTable and #_CFG.global.cargoTable > 0)
end

function FoundationClass.static:staticMethod(var)
	_debug(self, 1, 'entering FoundationClass.static:staticMethod() to do something, from ' .. tostring(self))
end

-- **************** declaration of private methods
local _amIPlausible = function (self)
	return (#_private[self].errors == 0)
end

_debug = function (self, level, text)
	if _private[self] and _private[self].dbg then
		local debugLevel = FoundationClass.globalConfig.debugLevel or self.class.myConfiguration.global.debugLevel
		if debugLevel and level <= debugLevel then
			_private[self].dbg:log(level, text)
		end
	else
		local debugLevel = FoundationClass.globalConfig.debugLevel or self.myConfiguration.global.debugLevel
		if debugLevel and level <= debugLevel then
			ClassDebug:log(level, text, tostring(self) .. '.static')
		end
	end
end

local _plausibilityTest = function (self, args)
	_debug(self, 1, 'entering FoundationClass._plausibilityTest(self, args)')
	local _CFG = self.class.myConfiguration
	local args = args
	-- first see, whether we are in the correct namespace (that is, if we are not initialzed from a datastore)
	if not _private[self].superHandler and not _private[self].loadedDataFromDataStore and _CFG.global.namespace and #_CFG.global.namespace > 0 and (_CFG.global.namespace ~= mw.title.getCurrentTitle().nsText) then
		self:addError("Diese Seite befindet sich in einem ungültigen Namesraum. Eingestellt ist ''" .. _CFG.global.namespace .. "'',  festgestellt wurde: " .. ((mw.title.getCurrentTitle().nsText and #mw.title.getCurrentTitle().nsText > 0) and mw.title.getCurrentTitle().nsText or 'keiner'))
	end
	-- prepare for the "show on select"-complex
	-- motivation:
	-- when you have a selector with a show on select field, sf shows some fields dynamically. sf even lets you set these dynamically shown fields
	-- to mandatory but checks for this only, if the field is shown.
	-- problem: so with this in mind, it is possible, that we have a mandatory field in our configuration with no value, but still be pausible
	-- since it only has to submit a value, if it is shown. but in the case it is shown, it has to submit a value
	-- what we do: build a table fieldnameToCheck : { showSelectorFieldname : valuetoshow }
	-- btw: this table fieldnameToCheck should only containe one element. if the programmer has the field "fieldnameToCheck" put in more than
	-- one show on select, the resulting form is erratic.
	local showOnSelectComplex = {}
	for param, paramdata in pairs(_CFG.parameter) do
		if not self.class.mySfDynamicFieldAttribute(self.class, param, 'disable', false) and paramdata.sf and paramdata.sf.show_on_select then
			local sos = self.class.mySfDynamicFieldAttribute(self.class, param, 'show_on_select', paramdata.sf.show_on_select)
			_debug(self, 3, '  got this in return: ' .. (type(sos) == 'string' and tostring(sos) or type(sos)))
			if type(sos) ~= 'table' then
				sos = tostring(sos)
				if not showOnSelectComplex[sos] then
					showOnSelectComplex[sos] = {}
				end
				-- this being a string makes sense only if input type is checkbox. we leave the responsibility to the programmer, though
				showOnSelectComplex[sos][param] = FoundationClass.globalConfig.selectedCheckboxSubmitValue
			else
				-- assume type table
				for selectedValue, showFields in pairs(sos) do
					if type(showFields) ~= 'table' then
						showFields = tostring(showFields)
						if not showOnSelectComplex[showFields] then
							showOnSelectComplex[showFields] = {}
						end
						showOnSelectComplex[showFields][param] = selectedValue
					else
						-- one last iteration
						for _, showField in pairs(showFields) do
							showFields = tostring(showFields)
							if not showOnSelectComplex[showField] then
								showOnSelectComplex[showField] = {}
							end
							showOnSelectComplex[showField][param] = selectedValue
						end
					end
				end
			end
		end
	end
	_debug(self, 3, '  have this showOnSelectComplex: ' .. FoundationClass.myTableTools.printTable(showOnSelectComplex))
	-- now do the plausibility test
	for param, paramdata in pairs(_CFG.parameter) do
		if not self.class.mySfDynamicFieldAttribute(self.class, param, 'disable', false) and paramdata.sf then
			if not self.class.mySfDynamicFieldAttribute(self.class, param, 'hidden', false) then
				-- it is not set to hidden, so evaluate
				if args[param] and (type(args[param]) ~= 'string' or mw.ustring.len(args[param]) > 0) then
				-- first, see if it's there
					local value = args[param]
					local values
					if paramdata.values then
						values = self.class.mySfDynamicFieldAttribute(self.class, param, 'values', paramdata.values)
					end
					if type(value) == 'string' and mw.ustring.match(value, '^[0-9]+$') then
						-- because argument data coming form a parameter and not from database means a number is displayed as a string
						-- problem: if we store an only numbers containing entry in values, this will fail, i.e. paramdate.values = {'0', '123'} can never work
						value = tonumber(value)
					end
					if paramdata.cardinality == 'single' then
						if values and not FoundationClass.myTableTools.inTable(values, value) and ((paramdata.sf.input_type ~= 'combobox' and paramdata.sf.input_type ~= 'tokens') or paramdata.sf.existing_values_only) then
							-- 'values'-restriction given but value given not in values
							-- only loophole being tokens or combobox and NOT existring_values_only
							self:addError("Ungültiger Wert für parameter ''" .. param .. "'' festgestellt: " .. mw.text.encode(tostring(value)))
						end
					elseif values and ((paramdata.sf.input_type ~= 'combobox' and paramdata.sf.input_type ~= 'tokens') or paramdata.sf.existing_values_only) then
						-- multivaluefield and 'values'-restriction given
						-- only loophole being tokens or combobox and NOT existring_values_only
						local vals = {}
						if type(args[param]) == 'table' then
							-- which can be, if we are initializing by data
							vals = args[param]
						else
							for fragment in mw.text.gsplit(args[param], _CFG.parameter[param].sf and _CFG.parameter[param].sf.delimiter or _CFG.global.delimiter, true) do
								if fragment and mw.ustring.len(mw.text.trim(fragment)) > 0 then
									fragment = mw.text.trim(fragment)
									if mw.ustring.match(fragment, '^[0-9]+$') then
										fragment = tonumber(fragment)
									end
									table.insert(vals, fragment)
								end
							end
						end
						for _, v in pairs(vals) do
							if not FoundationClass.myTableTools.inTable(values, v) then
								self:addError("Ungültiger Wert für parameter ''" .. param .. "'' festgestellt: " .. mw.text.encode(v))
							end
						end
					end
				elseif FoundationClass.myTableTools.inTable(_CFG.form.fieldOrder, param) and paramdata.severity == 'mandatory' then
					-- param not found but should be (since it is a form param) and it is also marked as mandatory and not set to hidden --> 
					-- before we assume error lets check for the one loophole: field could be part of a "show on select", thus being mandatory but not shown
					local issueError = true
					if showOnSelectComplex[param] then
						-- yeah, the field is part of a "show on select". we only issue an error, if its selector indicates a show
						issueError = false
						for fieldname, showValue in pairs(showOnSelectComplex[param]) do
							if tostring(args[fieldname]) == tostring(showValue) then
								-- so it is shown and therefore should contain a value
								issueError = true
							end
						end
					end
					if issueError then
						self:addError("Pflichtfeld ''" .. param .. "'' fehlt!")
					end
				end
			else
				_debug(self, 1, '  field ' .. param .. ' is set to hidden. Not processing!')
			end
		else
			_debug(self, 1, '  field ' .. param .. ' is disabled or no sf-table given. Not processing!')
		end
	end
	return _amIPlausible(self)
end

local _privateMethod = function (self)
	_debug(self, 1, 'entering FoundationClass._privateMethod() to do something')
end

-- **************** declaration of public methods
function FoundationClass:addCategory(category)
	_debug(self, 1, 'entering FoundationClass:addCategory(category) with category ' .. (category and tostring(category) or 'EMPTY') .. ', from ' .. tostring(self))
	local category = category
	if category and mw.ustring.len(category) > 0 then
		if _private[self].superHandler then
			_debug(self, 3, '  found handler ' .. tostring(_private[self].superHandler) .. ', so we are probably not on the right page to lease a category. omitting categorizing into category "' .. category .. '"')
			--_debug(self, 3, '  refering category to handler ' .. tostring(_private[self].superHandler))
			--_private[self].superHandler:addCategory(category)
		else
			table.insert(_private[self].categories, category)
		end
	end
	return true
end

function FoundationClass:addError(errortext)
	_debug(self, 1, 'entering FoundationClass:addError(errortext), from ' .. tostring(self))
	_debug(self, 3, '  with errortext ' .. tostring(errortext))
	local errortext = errortext
	if errortext and mw.ustring.len(errortext) > 0 then
		if _private[self].superHandler then
			_debug(self, 3, '  refering error to handler ' .. tostring(_private[self].superHandler))
			errortext = (self:getUid() and (self:getUid() .. ': ') or '') .. errortext
			_private[self].superHandler:addError(errortext)
		end
		-- have to store it myself, too. otherwise, I would always be plausible...
		table.insert(_private[self].errors, errortext)
	end
	return true
end

function FoundationClass:addInfobox()
	_debug(self, 1, 'entering FoundationClass:addInfobox(), from ' .. tostring(self))
	return false
end

function FoundationClass:addOutput(content)
	_debug(self, 1, 'entering FoundationClass:addOutput(), from ' .. tostring(self))
	if type(content) == 'table' then
		_private[self].output:node(content)
		return true
	elseif mw.ustring.len(tostring(content)) > 0 then
		_private[self].output:wikitext(content)
		return true
	end
	return false
end

function FoundationClass:addPageBody()
	_debug(self, 1, 'entering FoundationClass:addPageBody(), from ' .. tostring(self))
	_debug(self, 2, ' rendering errors and warnings and adding them to output')
	local frame = mw.getCurrentFrame()
	_private[self].output:node(self:renderErrors())
			:node(self:renderWarnings())
	if self:goodToGo() then
		_private[self].output:wikitext('Please implement method <code>addPageBody()</code> to display page content!')
		return true
	end
	return false
end

function FoundationClass:addError(errortext)
	_debug(self, 1, 'entering FoundationClass:addError(errortext), from ' .. tostring(self))
	_debug(self, 3, '  with errortext ' .. tostring(errortext))
	local errortext = errortext
	if errortext and mw.ustring.len(errortext) > 0 then
		if _private[self].superHandler then
			_debug(self, 3, '  refering error to handler ' .. tostring(_private[self].superHandler))
			errortext = (self:getUid() and (self:getUid() .. ': ') or '') .. errortext
			_private[self].superHandler:addError(errortext)
		end
		-- have to store it myself, too. otherwise, I would always be plausible...
		table.insert(_private[self].errors, errortext)
	end
	return true
end

function FoundationClass:addWarning(warning)
	_debug(self, 1, 'entering FoundationClass:addWarning(errortext), from ' .. tostring(self))
	_debug(self, 3, '  with warning ' .. tostring(warning))
	local warning = warning
	if warning and mw.ustring.len(warning) > 0 then
		if _private[self].superHandler then
			_debug(self, 3, '  refering error to handler ' .. tostring(_private[self].superHandler))
			warning = (self:getUid() and (self:getUid() .. ': ') or '') .. warning
			_private[self].superHandler:addError(warning)
		end
		-- have to store it myself, too. otherwise, I would always be plausible...
		table.insert(_private[self].warnings, warning)
	end
	return true
end

function FoundationClass:getCoreData()
	_debug(self, 1, 'entering FoundationClass:getCoreData(), from ' .. tostring(self))
	return _private[self].coreData
end

function FoundationClass:getUid()
	_debug(self, 1, 'entering FoundationClass:getUid(), from ' .. tostring(self))
	return _private[self].uid
end

function FoundationClass:goodToGo()
	_debug(self, 1, 'entering FoundationClass:goodToGo(), from ' .. tostring(self))
	return _amIPlausible(self) and _private[self].initialized
end

function FoundationClass:initFromArgs(args)
	_debug(self, 1, 'entering FoundationClass:initFromArgs(args), from ' .. tostring(self))
	local _CFG = self.class.myConfiguration
	local args = args
	if not args or type(args) ~= 'table' then
		local getArgs = require('Module:Arguments').getArgs
		local frame = mw.getCurrentFrame()
		args = getArgs(frame)
	end
	if not _private[self].loadedDataMyself then
		-- if we didn't load our data ourselves, assume that it comes from the store and assume, it's plausible
		_plausibilityTest(self, args)
		self:myPlausibilityTest(args)
	end
	if _amIPlausible(self) then
		-- first set my uid
		if not _private[self].coreData[FoundationClass.globalConfig.uidFieldName] then
			_private[self].coreData[FoundationClass.globalConfig.uidFieldName] = self:getUid()
		end
		-- if I use smw as store, also set my class
		if FoundationClass.usesDataStore(self.class, 'smw') and not _private[self].coreData[FoundationClass.globalConfig.smwClassProperty] then
			_private[self].coreData[FoundationClass.globalConfig.smwClassProperty] = self.class.name
		end
		-- get the data from args into _private[self].coreData
		for param, paramdata in pairs(_CFG.parameter) do
			if paramdata.cardinality == 'single' then
				_private[self].coreData[param] = args[param]
			else
				-- multivaluefield and given
				if type(args[param]) == 'table' then
					_private[self].coreData[param] = args[param]
				else
					local values
					if args[param] then
						values = {}
						for fragment in mw.text.gsplit(args[param], _CFG.parameter[param].sf and _CFG.parameter[param].sf.delimiter or _CFG.global.delimiter, true) do
							if fragment and mw.ustring.len(mw.text.trim(fragment)) > 0 then
								table.insert(values, mw.text.trim(fragment))
							end
						end
					end
					_private[self].coreData[param] = values
				end
			end
		end
		-- here we do some individual argument processing
		_debug(self, 3, ' calling self:myArgumentProcessing(_private[self].coreData)')
		_private[self].coreData = self:myArgumentProcessing(_private[self].coreData)
		_debug(self, 3, ' object initialized with data:<pre>' .. FoundationClass.myTableTools.printTable(_private[self].coreData) .. '</pre>')
		--this can cause fatal error, if coreData contains to much data, for instance references to title-objects
		_private[self].initialized = true
	end
	return _private[self].initialized
end

function FoundationClass:initFromData(data)
	-- this takes data that comes from a data store and initializes itself with it
	-- if we use data store smw, this makes a conversion from propery indexed data to parameter indexed data
	-- if we use both data stores, data from cargo has precedence
	_debug(self, 1, 'entering FoundationClass:initFromData() to do something, from ' .. tostring(self))
	_debug(self, 3, '  got data:<pre>' .. FoundationClass.myTableTools.printTable(data) .. '</pre>')
	local data = data
	_private[self].loadedDataFromDataStore = true
	if FoundationClass.usesDataStore(self.class, 'smw') then	-- if we have store cargo, we take the dat as is.
		-- transform the data array from { property_name : value } to { paramter : value }
		local properties = FoundationClass.smwGetProperty2ParameterTranslationTable(self.class)
		for property, param in pairs(properties) do
			local property_ = property
			local property = mw.ustring.gsub(property_, '_', ' ')
			if data[property_] and not data[param] then
				data[param] = data[property_]
				data[property_] = nil
			end
			if data[property] and not data[param] then
				data[param] = data[property]
				data[property] = nil
			end
		end
	end
	data = self:myDataAdjustments(data)
	if FoundationClass.myTableTools.size(data) > 0 then
		return self:initFromArgs(data)
	else
		return false
	end
end

function FoundationClass:initMyself(uid)
	_debug(self, 1, 'entering FoundationClass:initMyself() to fetch my own data from datastore, from ' .. tostring(self))
	local _CFG = self.class.myConfiguration
	local uid = uid or self:getUid()
	local myData = {}
	if uid and FoundationClass.usesDataStore(self.class, 'cargo') then
		_debug(self, 2, ' using datastore \'cargo\' with uid: ' .. uid)
		-- build the query
		local query = { tables = _CFG.global.cargoTable, fields = {}, where = ''}
		local wherefield = FoundationClass.globalConfig.uidFieldName
		local wherefor = '"' .. uid .. '"'
		query.where = wherefield .. '=' .. wherefor
		local declaration = FoundationClass.cargoGenerateTableStructure(self.class)
		for field, _ in pairs(declaration) do
			table.insert(query.fields, field)
		end
		local result = FoundationClass.myCargoUtil.query(query, {limit = 1})
		if result and result[1] and type(result[1]) == 'table' and FoundationClass.myTableTools.size(result[1]) > 0 then
			-- now we have data, call initFromData()
			_debug(self, 2, '  Got data from cargo query, storing it')
			myData = result[1]
		elseif not FoundationClass.usesDataStore(self.class, 'smw') then
			_debug(self, 2, '  Could not initialize by myself: Cargo query for \'' .. wherefield .. '=' .. wherefor .. '\' in table \'' .. _CFG.global.cargoTable .. '\' did not yield any result.')
			_debug(self, 3, '  Query was<pre>' .. FoundationClass.myTableTools.printTable(query) .. '</pre>')
			self:addError('Could not initialize by myself: Cargo query for \'' .. wherefield .. '=' .. wherefor .. '\' in table \'' .. _CFG.global.cargoTable .. '\' did not yield any result.')
		end
	end
	if uid and FoundationClass.usesDataStore(self.class, 'smw') then
		_debug(self, 2, ' using datastore \'smw\' with uid: ' .. uid)
		-- build the query
		local query = { select = {}, fields = {}, where = ''}
		query.select[FoundationClass.globalConfig.smwClassProperty] = self.class.name
		local wherefield = FoundationClass.globalConfig.uidFieldName
		local wherefor = '"' .. uid .. '"'
		query.select[wherefield] = wherefor 
		local properties = FoundationClass.smwGetProperty2ParameterTranslationTable(self.class)
		for property, _ in pairs(properties) do
			table.insert(query.fields, property)
		end
		local result = FoundationClass.mySmwUtil.query(query, {limit = 1})
		if result and result[1] and type(result[1]) == 'table' and FoundationClass.myTableTools.size(result[1]) > 0 then
			for property, smwdata in pairs(result[1]) do
				if not myData[property] then
					myData[property] = smwdata
				end
			end
		end
	end
	if not FoundationClass.myTableTools.size(myData) then
		if not uid then
			_debug(self, 2, '  Could not initialize by myself: No valid uid was provided. Was type \'' .. type(uid) .. '.')
			self:addError('Could not initialize by myself: No valid uid was provided. Was type \'' .. type(uid) .. '.')
		elseif FoundationClass.usesDataStore(self.class, 'both') then
			_debug(self, 2, '  Could not initialize by myself: Neither Cargo nor Smw provided any data. Was looking for \'' .. FoundationClass.globalConfig.uidFieldName .. ' with uid ' .. 
				uid .. ' in class ' .. self.class.name .. '.')
			self:addError('Could not initialize by myself: Neither Cargo nor Smw provided any data. Was looking for \'' .. FoundationClass.globalConfig.uidFieldName .. ' with uid ' .. 
				uid .. ' in class ' .. self.class.name .. '.')
		elseif FoundationClass.usesDataStore(self.class, 'cargo') then
			_debug(self, 2, '  Could not initialize by myself: Cargo query for \'' .. FoundationClass.globalConfig.uidFieldName .. '=' .. uid .. '\' did not yield any result.')
			self:addError('Could not initialize by myself: Cargo query for \'' .. FoundationClass.globalConfig.uidFieldName .. '=' .. uid .. '\' did not yield any result.')
		else
			_debug(self, 2, '  Could not initialize by myself: Smw query in class \'' .. self.class.name .. '\' for \'' .. FoundationClass.globalConfig.uidFieldName .. '=' .. uid .. '\' did not yield any result.')
			self:addError('Could not initialize by myself: Smw query in class \'' .. self.class.name .. '\' for \'' .. FoundationClass.globalConfig.uidFieldName .. '=' .. uid .. '\' did not yield any result.')
		end
	else
		-- now we have data, call initFromData()
		_debug(self, 2, '  Got data from query, calling self:initFromData(myData)')
		_private[self].loadedDataMyself = true
		return self:initFromData(myData)
	end
	return false
end

function FoundationClass:render()
	_debug(self, 1, 'entering FoundationClass:render(), from ' .. tostring(self))
	self:addOutput(self:renderCategories())
	local ret = tostring(_private[self].output)
	local debugLevel = FoundationClass.globalConfig.debugLevel or self.class.myConfiguration.global.debugLevel
	if debugLevel then
		ret = ret .. '\n\n' .. '<h2>Debug level is ' .. debugLevel .. '</h2>\n' .. ClassDebug:printLog(debugLevel)
	end
	return ret
end

function FoundationClass:renderCategories()
	_debug(self, 1, 'entering FoundationClass:renderCategories(), from ' .. tostring(self))
	node = mw.html.create('')
	if _private[self].categories and (#_private[self].categories > 0) and (self:getUid() == mw.title.getCurrentTitle().prefixedText) then
		for _, category in pairs(_private[self].categories) do
			node:wikitext('[[Category:' .. category .. ']]')
		end
	end
	return node
end

function FoundationClass:renderErrors()
	_debug(self, 1, 'entering FoundationClass:renderErrors(), from ' .. tostring(self))
	local _CFG = self.class.myConfiguration
	local error = require('Module:Error').error
	node = mw.html.create('')
	if #_private[self].errors > 0 then
		if _CFG.global.gardeningCategory and mw.title.getCurrentTitle().namespace ~= 10 then
			-- we have errors and we are currently not in template namespace. place article in gardening category
			self:addCategory(_CFG.global.gardeningCategory)
		end
		node:wikitext('<h2 style="font-weight:bold;background-color:light-red;color:red">Fehler</h2>')
		for _, str in pairs(_private[self].errors) do
			node:wikitext(error{ message=str, tag='div'})
		end
	end
	return node
end

function FoundationClass:renderWarnings()
	_debug(self, 1, 'entering FoundationClass:renderWarnings(), from ' .. tostring(self))
	node = mw.html.create('')
	if #_private[self].warnings > 0 then
		node:wikitext('<h2>Hinweise</h2>')
		local lis = mw.html.create('')
		for _, str in pairs(_private[self].warnings) do
			lis:tag('li')
				:wikitext(str)
				:done()
		end
		node:tag('ul')
			:node(lis)
	end
	return node
end

function FoundationClass:setSortkey(sortkey)
	_debug(self, 1, 'entering FoundationClass:setSortkey(sortkey) with sortkey ' .. (sortkey and tostring(sortkey) or 'EMPTY') .. ', from ' .. tostring(self))
	local sortkey = sortkey
	if sortkey and mw.ustring.len(sortkey) > 0 then
		if not _private[self].superHandler then
			local frame = mw.getCurrentFrame()
			frame:callParserFunction('DEFAULTSORT:' .. sortkey, 'noerror')
			-- foobar. but viable workarount until new ipso is running
			if mw.ext and mw.ext.displaytitle then
				mw.ext.displaytitle.set(sortkey)
			end
		end
	end
	return true
end

function FoundationClass:setUid(uid)
	_debug(self, 1, 'entering FoundationClass:setUid(uid), from ' .. tostring(self))
	if uid and (type(uid) == 'table' or type(uid) == 'integer') then
		_private[self].uid = uid
		return true
	end
	return false
end

function FoundationClass:storeData()
	_debug(self, 1, 'entering FoundationClass:storeData(), from ' .. tostring(self))
	local _CFG = self.class.myConfiguration
	local thisPage = mw.title.getCurrentTitle()
	local ret = false
	local lang = mw.language.getContentLanguage()
	if FoundationClass.usesDataStore(self.class, 'cargo') and thisPage.namespace == 10 and lang:ucfirst(thisPage.text) == lang:ucfirst(_CFG.template.name) then
		-- we must check for cargo data store first, because declaration must take place on namespace template where we have no data...
		_debug(self, 2, ' proceeding (cargo data store detected, poceeding to declare)')
		-- declaration of the cargo-table
		local declaration = self.class.cargoGenerateTableStructure(self.class)
		-- remember: self refers to an object not an class(!!!!) and this method is a static, so we have to call it directly and not with the colon operator
		local ret = FoundationClass.myCargoUtil.declare(_CFG.global.cargoTable, declaration)
		if mw.ustring.len(ret) > 0 then
			self:addWarning(ret)
		end
	end
	if _amIPlausible(self) and _private[self].initialized and not _private[self].loadedDataMyself then
		_debug(self, 2, ' proceeding (plausible, initialized and got data from extern)')
		if FoundationClass.usesDataStore(self.class, 'cargo') then
			-- now store the cargo data
			local stash = {}
			local declaration = FoundationClass.cargoGenerateTableStructure(self.class)
			for field, _ in pairs(declaration) do
				if _private[self].coreData[field] then
					if type(_private[self].coreData[field]) == 'table' then
						stash[field] = table.concat(_private[self].coreData[field], (_CFG.parameter[field].sf and _CFG.parameter[field].sf.delimiter or _CFG.global.delimiter))
					else
						stash[field] = _private[self].coreData[field]
					end
				end
			end
			-- individual adjustments
			stash = self:myStashAdjustments(stash, 'cargo')
			-- we dont want myStashAdjustments add any fields that is not a cargo field (in case it accidently added a field, that was meant for smw)
			if stash and type(stash) == 'table' then
				for field, _ in pairs(stash) do
					if not declaration[field] then
						stash[field] = nil
					end
				end
			end
			if stash and FoundationClass.myTableTools.size(stash) > 0 then
				local result = FoundationClass.myCargoUtil.store(_CFG.global.cargoTable, stash)
				if mw.ustring.len(result) > 0 then
					self:addWarning(result)
				end
				ret = true
			else
				ret = false
			end
		end	-- of if FoundationClass.usesDataStore(self.class, 'cargo') then
		if FoundationClass.usesDataStore(self.class, 'smw') then
			-- now store smw properties
			local stash = {}
			local properties = FoundationClass.smwGetProperty2ParameterTranslationTable(self.class)
			-- first get all parameter and their data in stash
			for _, parameter in pairs(properties) do
				if _private[self].coreData[parameter] then
					stash[parameter] = _private[self].coreData[parameter]
				end
			end
			-- individual adjustments, still with parameter as keys
			stash = self:myStashAdjustments(stash, 'smw')
			-- now convert the stash keys from parameter to property
			-- that also takes care of any fields, that were accidently added for a cargo store but have no property assigned
			if stash and FoundationClass.myTableTools.size(stash) > 0 then
				local stash_new = {}
				for property, parameter in pairs(properties) do
					stash_new[property] = stash[parameter]
				end
				stash = stash_new
			end
			if stash and FoundationClass.myTableTools.size(stash) > 0 then
				local result
				if _CFG.global.smwIsSubobject then
					result = FoundationClass.mySmwUtil.subobject(stash, self:getUid())
				else
					result = FoundationClass.mySmwUtil.set(stash)
				end
				if mw.ustring.len(result) > 0 then
					self:addWarning(result)
				end
				ret = true
			else
				ret = false
			end
		end	-- of if FoundationClass.usesDataStore(self.class, 'smw') then
	end
	return ret
end

function FoundationClass:method()
	_debug(self, 1, 'entering FoundationClass:method() to do something, from ' .. tostring(self))
	return true
end

-- these are special methods, so they are placed here. they are kind of abstract-ish in a way, that they thow a lua exception
-- if you don't have them implemented on your inherited class
function FoundationClass.static:mySfDynamicFieldAttribute(fieldname, attribute, value)
	_debug(self, 1, 'ERROR: entering FoundationClass.static:mySfDynamicFieldAttribute(fieldname, attribute, value), from ' .. tostring(self))
	error('You have to implement ' .. tostring(self.class) .. '.static:mySfDynamicFieldAttribute(fieldname, attribute, value). See Module:Foundationclass for documentation on this! (calling from ' .. tostring(self) .. ')', 1)
end

function FoundationClass:myArgumentProcessing(coreData)
	_debug(self, 1, 'ERROR: entering FoundationClass:myArgumentProcessing(coreData), from ' .. tostring(self))
	error('You have to implement ' .. tostring(self.class) .. ':myArgumentProcessing(coreData). See Module:Foundationclass for documentation on this! (calling from ' .. tostring(self) .. ')', 1)
end

function FoundationClass:myDataAdjustments(data)
	_debug(self, 1, 'ERROR: entering FoundationClass:myDataAdjustments(data), from ' .. tostring(self))
	error('You have to implement ' .. tostring(self.class) .. ':myDataAdjustments(coreData). See Module:Foundationclass for documentation on this! (calling from ' .. tostring(self) .. ')', 1)
end

function FoundationClass:myPlausibilityTest(args)
	_debug(self, 1, 'ERROR: entering FoundationClass:myPlausibilityTest(args), from ' .. tostring(self))
	error('You have to implement ' .. tostring(self.class) .. ':myPlausibilityTest(args). See Module:Foundationclass for documentation on this! (calling from ' .. tostring(self) .. ')', 1)
end

function FoundationClass:myStashAdjustments(stash)
	_debug(self, 1, 'ERROR: entering FoundationClass:myStashAdjustments(stash), from ' .. tostring(self))
	error('You have to implement ClassName:myStashAdjustments(stash). See Module:Foundationclass for documentation on this! (calling from ' .. tostring(self) .. ')', 1)
end

return FoundationClass