Module:Sandbox/Was a bee/tree

Source: Wikipedia, the free encyclopedia.

local p = {}

datas = {}


-- These setdata() and getdata() functions are used to access (read and write) to "datas" table
function p.setdata(n, key, val)
	if datas[n] == nil then
		datas[n] = {}
	end
	datas[n][key] = val
end

function p.getdata(n, key, defval)
	if  datas[n] == nil or datas[n][key] == nil then
		return defval
	else
		return datas[n][key] 
	end
end

function p.children(query, itemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
 --------------------------------------------------------------------------------
 -- children function which listing all childrens that certain item has.
 -- Not listing grand childrens, or grand-grand childrens. Listing only childrens.
 -- Final returning value "childrens" is like {Q123123, Q3984198237, Q1874138746} 
 --------------------------------------------------------------------------------
	local childrens = {}
	local entity = mw.wikibase.getEntity( itemId )
	local claims = nil
	local x = 1
	local tmpValue = {}
	
	---------------------------------------------------------------
	-- Start collecting data from the retrieved entity, and save that into the datas[] table.
	-- Because getEntity() function is EXPENSIVE, so here, collecting all data at once.
	-- https://en.wikipedia.org/wiki/WP:EXPENSIVE
	-- https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua#mw.wikibase.getEntity
	----------------------------------------------------------------
	local globalSiteId_1 = lang1 .. 'wiki'
	local globalSiteId_2
	
	if lang2 and lang2 ~= '' then
		globalSiteId_2 = lang2 .. 'wiki'
	else
		globalSiteId_2 = nil
	end
	
	p.setdata(itemId, "sitelink_1", entity:getSitelink( globalSiteId_1 ))
	p.setdata(itemId, "label_1", entity:getLabel( lang1 ))
	if lang2 and lang2 ~= '' then
		p.setdata(itemId, "sitelink_2", entity:getSitelink( globalSiteId_2 ))
		p.setdata(itemId, "label_2", entity:getLabel( lang2 ))
	end
	
	-- ★★★★ Start source specific code ★★★★ --
	------------TA98------------
	claims = entity['claims']['P1323'] --TA98 ID
	if claims then
		for  _,claim in pairs( claims ) do
			tmpValue[x] = claim.mainsnak.datavalue.value
			x = x + 1
		end
		p.setdata(itemId, "TA98", tmpValue)
	end
	
	tmpValue = {} --initialize
	x = 1 --initialize
	claims = nil --initialize
	
	------------MeSH------------
	--claims = entity['claims']['P672'] --MeSH Code
	--if claims then
	--	for  _,claim in pairs( claims ) do
	--		tmpValue[x] = claim.mainsnak.datavalue.value
	--		x = x + 1
	--	end
	--	p.setdata(itemId, "MeSH", tmpValue)
	--end
	--
	--tmpValue = {} --initialize
	--x = 1 --initialize
	--claims = nil --initialize
	-- ★★★★ End source specific code ★★★★ --
	
	---------------------------------------------------------------		
	-- End collecting data
	---------------------------------------------------------------
	
	--mw.logObject(datas[itemId])
	---------------------------------------------------------------		
	-- Start collecting childrens
	---------------------------------------------------------------
	claims = entity['claims'][property]
	
	--local claims = wikidata.getClaims(query) 
	if not claims then
		return {}
	end
	
	local has_source
	
	if sources then -- If input setting about "sources" exists
		has_source = false -- default
	else
		has_source = true -- when setting doesn't requires sources, assuming as if all cliams are sourced
	end
	
	for  _,claim in pairs( claims ) do
		if sources then -- If input setting about "sources" exists
			has_source = false -- reset
			
			-- ★★★★ Start source specific code ★★★★ --
			if claim['references'] and claim['references'][1]['snaks']['P248'] then -- If P248 ("stated in") claim exists
				if claim['references'][1]['snaks']['P248'][1]['datavalue']['value']['id'] == 'Q286567' then -- If it is TA98 ('Q286567')
					has_source = true --If the claim is sourced by Terminologia Anatomica, then "has_source" is true
					p.setdata('Q' .. claim.mainsnak.datavalue.value['numeric-id'], 'sourcedBy', 'TA98')-- set datas table, like datas[Q1234]['sourceType'] = 'TA98'
				end
			end
			-- ★★★★ End source specific code ★★★★ --
		end
		
		--Adding item only when the claim is sourced by specific literature (e.g. Terminologia Anatomica)
		--or adding all item if input setting doesn't need source
		if has_source then 
			local value = claim.mainsnak.datavalue.value
			local nextitem = 'Q' .. value['numeric-id']

			childrens[x] = nextitem
			x = x + 1
		end
	end
	--mw.logObject(datas[itemId])
	--mw.log("childrens are : ")
	--mw.logObject(childrens)
	return childrens
	---------------------------------------------------------------		
	-- End collecting childrens
	---------------------------------------------------------------
end
		
-- generator, see http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators for lua doc
-- definition a function that when called several times will generate a sequence of strings
 
-- gensymbols = create_gensymbols("ABC")
-- gensymbols() == "A" 
-- gensymbols() == "B" g
-- gensymbols() == "C" 
-- gensymbols() == "AA" '
-- gensymbols() == "BABBA"
-- ...
function p.gensymbols(chars)
	local i = 0
	local charset = chars
 
	local generator = function ()
		local symbol = ""
		local rest
 
		rest = i
 
		repeat
			local j 
			j = rest % string.len(charset)
			rest = math.floor(rest / string.len(charset))
			symbol = symbol .. string.sub(charset, j+1, j+1)
		until rest <= 0
		i = i + 1
		return symbol
	end
	return generator
end

-------------------------------------------------------------------------------------------	
-- Start definition of itemOutput function which returns text like '[[' .. link .. '|' .. label .. ']]'
-------------------------------------------------------------------------------------------	
function p.itemOutput(item, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
		
	local globalSiteId_1 = lang1 .. 'wiki'
	local globalSiteId_2 = lang2 .. 'wiki'
	--local lang_2 = 'en'
	--local langWiki = '//'..lang..'.wikipedia.org' -- if lang is 'fr', then this is '//fr.wikipedia.org' used to compare with mw.site.server
	
	--local currentEntity = mw.wikibase.getEntityObject( item )
	local currentLabel
	local currentLabel_temp
	
	local content
	local refTable = {}
	local referenceText = ''
	local pencilText
	if pencil == 'none' then 
		pencilText = ''
	else --(P527 "has part" or P361 "is part of")
		pencilText = '[[File:Blue pencil.svg|frameless|text-top|10px|link=https://www.wikidata.org/wiki/' .. item .. '#P527]]'
	end
	
	refStyle = 'asdasdsa'
	--mw.log('refStyle is: ' .. refStyle)
	
	if refStyle and refStyle ~= 'none' then
	-- ★★★★ Start source specific code ★★★★ --
		if p.getdata(item, "sourcedBy", nil ) and p.getdata(item, "sourcedBy", nil )  == 'TA98' then
			--mw.log('datas[] is: ')
			--mw.logObject(datas)
			mw.log('<br/>')
			mw.log('<br/>')
			mw.log('item is: ')
			mw.logObject(item)
			mw.log('datas[item] is: ')
			mw.logObject(datas[item])
			mw.log('datas[item]["TA98"] is: ')
			mw.logObject(datas[item]["TA98"])
				refTable = p.getdata(item, "TA98", nil )
			mw.log('refTable is: ')
			mw.logObject(refTable)
			mw.log('<br/>')
			mw.log('<br/>')
			if refTable and refTable ~=  '' then
				for  i, TA98ID in pairs( refTable ) do
					refTable[i] = string.sub(refTable[i], 2) --Remove first character "A" from ID
					refTable[i] = '[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/' .. refTable[i] .. '%20Entity%20TA98%20EN.htm' .. ' ' .. 'A' .. refTable[i] ..']' -- Creating wikitext which links to TA98 official site
					--mw.log('refTable is :' .. tostring(table.concat(refTable)))
				end
				-- Converting multipul IDs into one line string, separated by comma, space ", "
				referenceText = tostring( table.concat( refTable, ', ' ) )
				referenceText = referenceText .. ' [[Terminologia Anatomica|TA98]], 1998'
				if refStyle == 'normal' then
					referenceText = tostring( mw.getCurrentFrame():extensionTag( 'ref', referenceText, {} ) ) 
				else
					referenceText = mw.getCurrentFrame():expandTemplate{ title = 'efn', args = {referenceText} }
				end
			else
				referenceText = ''
			end
		else
			referenceText = ''
		end
	-- ★★★★ End source specific code ★★★★ --
	end
	
	--mw.log('referenceText is :' .. referenceText)
	
	--Link creation priority. Followings is an example.
	--'ja'(Japanese) represents your own language, and 'en'(English) represents other language which is familiar with your readers.
	--This section would be better to customize for your own readers.
	
	--1. [[:ja:Sitelink(ja)|Sitelink(ja)]]           --If local sitelink data exists, use that.
	--2. [[:ja:Label(ja)|Label(ja)]](en link)        --If local sitelink data doesn't exist, but local label data exists, use that (with en link).
	--3. Sitelink(en) (en link)  --If both local language data doesn't exist, use sitelink to English Wikipedia
	--4. Label(en)(en link)        --If such data doesn't exist, use English label
	--5. Q123456                     --If all of such kind data doesn't exist, use bare item ID
	
	currentLabel = p.getdata(item, "sitelink_1", nil )
	-- wd._references( {item, 'P361'} ) 
	if currentLabel ~= nil then
		content = '[[:' .. lang1 .. ':' .. currentLabel .. '|' .. currentLabel .. ']]'
		content = content .. ' ' .. referenceText
		content = content .. ' ' .. pencilText 
		return content ----1. [[:ja:Sitelink(ja)|Sitelink(ja)]] style 
	else
		currentLabel = p.getdata(item, "label_1", nil )
		if currentLabel ~= nil then
			content = '[[:' .. lang1 .. ':' .. currentLabel .. '|' .. currentLabel .. ']]'
			currentLabel_temp = p.getdata(item, "sitelink_2", nil )
			if currentLabel_temp ~= nil then
				content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel_temp .. '|' .. lang2 .. ']])' ----adding (en) link
			end
			content = content .. ' ' .. referenceText
			content = content .. ' ' .. pencilText 
			return content ----2. [[:ja:Label(ja)|Label(ja)]](en link) style
		else
			currentLabel = p.getdata(item, "sitelink_2", nil )
			if currentLabel ~= nil then
				content = currentLabel
				content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel .. '|' .. lang2 .. ']])' ----adding (en) link
				content = content .. ' ' .. referenceText
				content = content .. ' ' .. pencilText 
				return content ----3. Sitelink(en) (en link) style
			else
				currentLabel = p.getdata(item, "label_2", nil )
				if currentLabel ~= nil then
					content = currentLabel
					content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel .. '|' .. lang2 .. ']])' ----adding (en) link
					content = content .. ' ' .. referenceText
					content = content .. ' ' .. pencilText 
					return content ----4. Label(en)(en link) style
				else
					content = item
					content = content .. ' ' .. referenceText
					content = content .. ' ' .. pencilText 
					return content --5. Q123456 style
				end
			end
		end
	end
end
-------------------------------------------------------------------------------------------	
-- End definition of itemOutput function which returns text like '[[' .. link .. '|' .. label .. ']]'
-------------------------------------------------------------------------------------------	

function p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
-------------------------------------------------------------------------------------------	
-- Start preparation of functions
-------------------------------------------------------------------------------------------	

	

		----- topological sort and meta data of the DAG ( https://en.wikipedia.org/wiki/Topological_sorting )
		
	--This firstPass() function is called only once, at first
	local function firstPass( query, firstItemId, item_repr )
	
	
	    local content = p.itemOutput(firstItemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
 
	    local opened = 1 -- This means the downstream branch of the item is under processing.
						 -- If encountering opened item through the processing, it indicates that infinite loop exists.   
						 
	    local closed = 2 -- This means the processing of all downstream branches of the item reached to its end.
						 -- The downest item is showed with " ? " in tree.
						 
	    local incomplete = 3 -- This means the processing of all downstream branches of the item reached to its end.
							 -- If more downstream branches exist but not processed (because of limitation from maxdepth),
							 -- then the downest item showed with "…" in tree.
							 
	    local marks = {} --opened(1) or closed(2) or incomplete(3) for each QID (e.g. marks[Q12234234] -> closed)
		
 
	    --local datas = {} --datas[QID]["nparents"], datas[QID]["symbol"], datas[QID]["looped"], datas[QID]["rank"], datas[QID]["status"]
						--datas[QID]["nparents"] represents "number of parents" of certain item. This is normally 1.
						--If this is 2 or more, it means that the exact same item appears several times in defferent branches of the tree repeatedly.
						
		local childrens = {} -- for example, childrens[Q1234] contains data like {Q123123, Q3984198237, Q1874138746}, three childrens of Q1234
 		
		--while there are unmarked nodes do
		--    select an unmarked node n
		--    visit(n) 
		--function visit(node n)
		--    if n has a temporary mark then stop (not a DAG)
		--    if n is not marked (i.e. has not been visited yet) then
		--        mark n temporarily
		--        for each node m with an edge from n to m do
		--            visit(m)
		--        mark n permanently
		--        add n to head of L
 
		-- this function 
		-- * visits and builds the tree, DAG or graph,
		-- * in the same pass, computes a topological ordering for the DAG
		-- * annotates the nodes with informations
	    function visit(n, depth, rank)
	    	--mw.log("depth is " .. depth ..  ">=" .. "maxdepth is " .. maxdepth) 
 
			--mw.log(n .. ": mark is " .. tostring(marks[n]))
			if marks[n] == opened then 
				p.setdata(n, "status", "loop")
				p.setdata(n, "rank", rank)
				return rank
			elseif marks[n] ~= closed then
				marks[n] = opened
 
				childrens[n] = p.children( query, n, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2)
				for _, node in ipairs(childrens[n]) do
					p.setdata(node, "nparents", 
						p.getdata(node, "nparents", 0) + 1
					) 
					--mw.log(node .. ": nparents is " .. tostring(p.getdata(node, "nparents", "aaa")))
					if depth <= maxdepth then
						rank = visit(node, depth + 1, rank + 1)
					end
				end
 
				if depth <= maxdepth then
					if p.getdata(n, "status", "complete") ~= "loop" then
						p.setdata(n, "status", "complete")
					end
					marks[n] = closed
				else
					p.setdata(n, "status", "incomplete")
					marks[n] = incomplete
				end
 
				p.setdata(n, "rank", rank)
			end
			return rank + 1
		end
		p.setdata(firstItemId, "nparents", 0)
		visit(firstItemId, 1, 0)
 
		return datas, childrens
	end

	local langobj = mw.language.new(lang1)
	-- link inside tree 
	local function formatSymbol(prefix)
		return '<span class="Unicode"><small>(' .. prefix .. ")</small></span>"
	end
	local function genAnchor(id)
		return '<span id="' .. firstItemId .. id .. '"></span>' 
	end
	local function anchorLink(id, content)
		return "[[#" .. firstItemId .. id .. "|" .. content .. "]]"
	end
	local function fmtTreeLinks(content)
		return mw.text.tag("sup", {}, content)
	end
 
	local function renderTree( itemId, datas, children, alreadyOuted, gs )
		local content = p.itemOutput( itemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
		local state
 
		if datas[itemId]["status"] ~= nil then
			state = datas[itemId]["status"] 
		end
		--mw.log(itemId .. ": status is " .. state )
 
		if 	datas[itemId] ["nparents"] > 1 and datas[itemId]["symbol"] == nil then
			p.setdata(itemId, "symbol", gs() )
		end
		-- prevent infinite loops in non DAGs
		if state == "loop" then
			if datas[itemId]["looped"] == nil then
				datas[itemId]["looped"] = "treated"
				content =  fmtTreeLinks(" ∞") .. " " .. content .. genAnchor(itemId)
			else
				return content ..  fmtTreeLinks("∞" ..	" ↑ " .. anchorLink(itemId, formatSymbol( datas[itemId]["symbol"] )))
			end
		elseif state == "incomplete" or state == "unvisited" then
			content = content .. " " .. fmtTreeLinks("…")
			return content
		end
 
		-- has no chilren ? display as leaf
		if children[itemId] ~= nil and table.getn(children[itemId]) == 0 then
			--return " " .. content .. " ? " -- would be great to use "?b, but font problem
			return " " .. content
		end
 
 
		datas[itemId] ["nparents"] = datas[itemId]["nparents"] - 1
 
	    local parts = {}
 
		-- sort children topologycally
		if alreadyOuted[itemId] == nil then
 
 			local langobj = mw.language.new(lang1)
		    local prefix = " "
		    if datas[itemId] ["nparents"] > 0 then
		    	local arrow = langobj:getArrow()
		    	prefix = fmtTreeLinks(genAnchor(itemId) .. " " .. arrow .. " " .. formatSymbol(datas[itemId]["symbol"]))
	    	end
				
			order = children[itemId]
	    	table.sort(order, function (a, b) return datas[a]["rank"] < datas[b]["rank"] end )
 
		    for i, childId in ipairs(order) do
		        table.insert( parts, renderTree( childId, datas, children, alreadyOuted, gs ) )
		    end
	 
			if direction == "child" or direction == "brother" then 
				local l = table.maxn( parts )
				for i = 1,(l - 1) do
					parts[i] = mw.text.tag( 'li', {}, parts[i] )
				end
				parts[l] = mw.text.tag( 'li', { class = 'lastline' }, parts[l] )
				alreadyOuted[itemId] = prefix .. " " .. content  .. mw.text.tag( 'ul', {}, table.concat( parts ) )
			elseif direction == "parent" then 				
				local texttmp
				local matchCount = 0
				--mw.log("parts is: ")
				--mw.logObject(parts)
				texttmp = mw.text.tag( 'ul', {}, mw.text.tag( 'li', { class = 'lastline' }, content ) )
				
				alreadyOuted[itemId], matchCount = string.gsub(table.concat( parts ), "(</li></ul>)", texttmp .. "%1", 1)
				--mw.log('matchCount is:')
				--mw.log(matchCount)
				if matchCount == 0 then
					alreadyOuted[itemId] = prefix .. " " .. string.gsub(table.concat( parts ), "$", texttmp, 1)
				end
				alreadyOuted[itemId] = prefix .. " " .. alreadyOuted[itemId]
				--mw.log("alreadyOuted[itemId] is: ")
				--mw.logObject(alreadyOuted[itemId])
			end
			
		end
 
		if datas[itemId] ["nparents"] <= 0 or state == "loop" then
			return alreadyOuted[itemId]
		else 
			return content .. " " .. fmtTreeLinks(anchorLink(itemId, formatSymbol(datas[itemId]["symbol"])) .. " " .. langobj:getArrow(forward))
		end
	end
-------------------------------------------------------------------------------------------	
-- End preparation of functions
-------------------------------------------------------------------------------------------	
 
-------------------------
-- Start creating tree --
-------------------------
	-- gen = p.gensymbols("??")
	-- gen = p.gensymbols("12")
	-- gen = p.gensymbols("★☆?")
	-- These symbols are used as link button for "jumping" from certain branch to other branch.
	-- "jumping" functionality is used when same item appears multipul times in one tree.
	local gen = p.gensymbols("@*#") 
 
	local children 
	datas, children = firstPass( query, firstItemId, gen)
	-- alreadyOuted is the table which contains html code for each QID.
	-- For example, like this... alreadyOuted[Q1234] = "<li>[[Human body]]</li>"
	-- Actually items which located more nearer to the root (to say, FirstItemId) contain html codes of their branchs within it. 
	-- For example, like this... alreadyOuted[Q1234] = "<li>[[Human body]] <ul><li>[[Nervous system]]</li></ul></li>"
	-- This is done by recursive calling of renderTree() function inside the renderTree() function.
	local alreadyOuted = {} 
	rendering = {} -- What does this do?
	return  renderTree( firstItemId, datas, children, alreadyOuted, gen)
-----------------------
-- End creating tree --
-----------------------
end

function p.main( frame )
------------------------------------
-- Start adjustments of arguments 
------------------------------------
    local frame = frame:getParent()
    local query = frame.args
	local sources
	local refStyle -- Option for refrence style. "none" or "normal" or other
	local pencil -- if pencil == "none", wikidata-linked pencil image is not shown 
    local maxdepth
    local lang1
    local lang2
	local firstItemId = query.items
	
	if firstItemId == nil or firstItemId == '' then
		--If QID is not defined, using the QID of the item which is connected to the current page.
		firstItemId = mw.wikibase.getEntityIdForCurrentPage() 
		if not firstItemId then
			return 'No items passed as parameter' --If there is no connected wikidata page, abort.
		end
    end
    --local firstItemsId = mw.text.split( query.items, ' ' )
    --if firstItemsId[1] == nil or firstItemsId[1] == '' or table.maxn( firstItemsId ) == 0 then
	--	--If QID is not defined, using the QID of the item which is connected to the current page.
	--	firstItemsId[1] = mw.wikibase.getEntityIdForCurrentPage() 
	--	if not firstItemsId[1] then
	--		return 'No items passed as parameter' --If there is no connected wikidata page, abort.
	--	end
    --end
	
	--for i, item in pairs( firstItemsId ) do
	if tonumber(firstItemId) then --legacy format
		firstItemId = 'Q'.. tostring(firstItemId)
	end
	--end
	
	if tonumber(query.childProperty) then --legacy format
		query.childProperty = 'P'.. tostring(query.childProperty)
	end
	
	if tonumber(query.parentProperty) then --legacy format
		query.parentProperty = 'P'.. tostring(query.parentProperty)
	end
	
	if query.childDepth and query.childDepth ~= '' then
		query.childDepth = tonumber( query.childDepth )
	else
		query.childDepth = 4
	end
	
	if query.parentDepth and query.parentDepth ~= '' then
		query.parentDepth = tonumber( query.parentDepth )
	else
		query.parentDepth = 2
	end
	
	if query.brotherDepth and query.brotherDepth ~= '' then
		query.brotherDepth = tonumber( query.brotherDepth )
	else
		query.brotherDepth = 0
	end
	
	if query.sources and query.sources ~= '' then
		sources = mw.text.split( query.sources, ' ' )
	else
		sources = nil
	end
	
	if query.refStyle and query.refStyle ~= '' then
		if query.refStyle == 'none' then
			refStyle = 'none'
		elseif query.refStyle == 'normal' then
			refStyle = 'normal'
		else
			refStyle = mw.text.split( query.sources, ' ' )
		end
	else
		refStyle = 'normal'
	end
	
	if query.pencil and query.pencil ~= '' then
		pencil = tostring(query.pencil)
	else
		pencil = nil
	end
	
	if query.lang1 and query.lang1 ~= '' then
		lang1 = query.lang1
	else
		lang1 = mw.language.getContentLanguage().code
	end
	
	if query.lang2 and query.lang2 ~= '' then
		lang2 = query.lang2
	else
		lang2 = 'en'
	end
	
----------------------------------
-- End adjustments of arguments 
----------------------------------

-------------------------
-- Start creating tree --
-------------------------
    local content = ''
    local parent_content = nil
    local child_content = nil
	local brother_content = nil
	local direction -- tree searching direction. "child", "parent" or "brother"
	local property -- property used to search items. For example "P527" (has part) or "P361" (part of)
	
	--------------------------------------------------
	--Creating tree for parent side (upstream side)
	--------------------------------------------------
	if query.parentProperty and query.parentProperty ~= '' then
		parent_content = ''
		direction = "parent"
		property = query.parentProperty
		maxdepth = query.parentDepth
		--for _, item in pairs( firstItemsId ) do
		   parent_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		--end
	end
	
	--------------------------------------------------
	--Creating tree for brother side (substream side)
	--------------------------------------------------
	-- Brother is, as definition, "parent's child". So both query.parentProperty and query.childProperty is needed.
	if query.brotherDepth and query.brotherDepth >= 0 and query.parentProperty and query.parentProperty ~= '' and query.parentDepth >= 1 and query.childProperty and query.childProperty ~= '' then
	
		brother_content = ''
		direction = "brother"
		maxdepth = query.brotherDepth
		property = query.parentProperty
		
		-- Get parent QID
		local parents = p.children(query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
		--local currentEntity = mw.wikibase.getEntityObject( firstItemId )
		--local claims = currentEntity['claims'][query.parentProperty]
		--if claims then -- If firstItemId has parent
		--	local x = 1
		--	local parents = {}
		--	for  _,claim in pairs( claims ) do -- Check all parent claims and get QID from there. And stored it into parents[] table.
		--			local value = claim.mainsnak.datavalue.value
		--			local nextitem = 'Q' .. value['numeric-id']

		--			parents[x] = nextitem
		--			x = x + 1
		--	end
			
			local currentEntity
			local claims
			local x = 1
			local brothers, brothersPart = {}
			property = query.childProperty
			mw.log("parents is :")
			mw.logObject(parents)
			-- Get brother QID
			for  _,parent in pairs( parents ) do
				
				brothersPart = p.children(query, parent, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
				mw.log("brothersPart is :")
				mw.logObject(brothersPart)
				--currentEntity = mw.wikibase.getEntityObject( parent )
				--claims = currentEntity['claims'][query.childProperty] -- Get child property in parent entity. Parent's child is brother.
				for  _,bro in pairs( brothersPart ) do -- Check all  child claims and get QID from there. And stored it into brothers[] table.
					--local value = claim.mainsnak.datavalue.value
					--local nextitem = 'Q' .. value['numeric-id']
					
					if bro ~= firstItemId then -- One of parent's child is ownself. It is not brother. So exclude that.
						brothers[x] = bro
						x = x + 1
					end
				end
			end
			
			mw.log("brothers is :")
			mw.logObject(brothers)
			-- Creating trees for brother
			local l = table.maxn( brothers )
			if l >= 1 then
				for i = 1,(l - 1) do
					brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[i], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
				end
				brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[l], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
			end
				
		--end
	end
	
	--------------------------------------------------
	--Creating tree for child side (downstream side)
	--------------------------------------------------
	if query.childProperty and query.childProperty ~= '' then
		child_content = ''
		direction = "child"
		property = query.childProperty
		maxdepth = query.childDepth
		if brother_content and brother_content ~= '' then
		   child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		else
		   child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		end
	end
	
	mw.log("parent_content is: ")
	mw.logObject(parent_content)
	mw.log("")
	mw.log("")
	mw.log("child_content is: ")
	mw.logObject(child_content)
	mw.log("")
	mw.log("")
	mw.log("brother_content is: ")
	mw.logObject(brother_content)
	mw.log("")
	mw.log("")
	
	--------------------------------------------------
	-- Combine parent and child (and brother) trees 
	--------------------------------------------------
	if parent_content and parent_content ~= '' and child_content and child_content ~= '' then
		-- Append child-tree to parent-tree.
		-- What is doing here is only replacing the last item (final '<li class=\"lastline\">...</li>') in "parent_content", by "child_content"
		-- But because of Lua regex function is not high functioning, bit complicated process is done here.
		local a = 0
		local b = 0
		
		-- Finding start position of final item by searching <li class=\"lastline\"> from the left to the right through text. 
		while string.find(parent_content, '<li class=\"lastline\">', b) do
			mw.log(a .. ' ' .. b)
			a, b = string.find(parent_content, '<li class=\"lastline\">', b)
		end
		mw.log(a .. ' ' .. b)
		
		local c = 0
		local d = 0
		-- Finding end position of final item by searching </ul></li>. 
		c, d = string.find(parent_content, '</ul></li>.*$') 
		--mw.log(c .. ' ' .. d)
		
		-- If c, d == nil, it means "parent_content" is one level, is not multipul level tree.
		-- In other words, it contains only one item which is correponding to "firstItemId".
		-- In such case, ignore "parent_content".
		-- Ignore "brother_content" also. Because if no parent known, no brother known.
		if c ~= nil and d ~= nil then 
			-- Replacing final item of "parent_content" by "child_content".
			-- If "brother_content" exists, inserts it just after the "child_content".
			if brother_content and brother_content ~= '' then
				content = string.sub(parent_content, 1, a - 1) .. brother_content .. child_content .. string.sub(parent_content, c)
			else
				content = string.sub(parent_content, 1, a - 1) .. child_content .. string.sub(parent_content, c)
			end
		else
			content = child_content
		end
	end
	
	mw.log("content after is: ")
	mw.logObject(content)
	
	--------------------------------------------------
	-- Final formatting and adding {{reflist}} like template at the bottom 
	--------------------------------------------------
	if parent_content or child_content then
		res =  mw.text.tag( 'div', { class = 'treeview' }, mw.text.tag( 'ul', {}, content ) )
		
		if sources then -- if sources are shown after each items like [1], then put {{reflist}} like template at the bottom 
			res = res .. frame:expandTemplate{ title = 'Hidden', args = { 'Sources', frame:expandTemplate{ title = 'notelist', args = {} } } }
		end
	end
-----------------------
-- End creating tree --
-----------------------
    return res
end

return p