text: SET ANSI_NULLS ON
  GO
  SET QUOTED_IDENTIFIER ON
  GO
  CREATE PROCEDURE [RDF.].[GetDataRDF]
  	@subject BIGINT=NULL,
  	@predicate BIGINT=NULL,
  	@object BIGINT=NULL,
  	@offset BIGINT=NULL,
  	@limit BIGINT=NULL,
  	@showDetails BIT=1,
  	@expand BIT=1,
  	@SessionID UNIQUEIDENTIFIER=NULL,
  	@NodeListXML XML=NULL,
  	@ExpandRDFListXML XML=NULL,
  	@returnXML BIT=1,
  	@returnXMLasStr BIT=0,
  	@dataStr NVARCHAR (MAX)=NULL OUTPUT,
  	@dataStrDataType NVARCHAR (255)=NULL OUTPUT,
  	@dataStrLanguage NVARCHAR (255)=NULL OUTPUT,
  	@RDF XML=NULL OUTPUT
  AS
  BEGIN
  	-- SET NOCOUNT ON added to prevent extra result sets from
  	-- interfering with SELECT statements.
  	SET NOCOUNT ON;
  
  	/*
  
  	This stored procedure returns the data for a node in RDF format.
  
  	Input parameters:
  		@subject		The NodeID whose RDF should be returned.
  		@predicate		The predicate NodeID for a network.
  		@object			The object NodeID for a connection.
  		@offset			Pagination - The first object node to return.
  		@limit			Pagination - The number of object nodes to return.
  		@showDetails	If 1, then additional properties will be returned.
  		@expand			If 1, then object properties will be expanded.
  		@SessionID		The SessionID of the user requesting the data.
  
  	There are two ways to call this procedure. By default, @returnXML = 1,
  	and the RDF is returned as XML. When @returnXML = 0, the data is instead
  	returned as the strings @dataStr, @dataStrDataType, and @dataStrLanguage.
  	This second method of calling this procedure is used by other procedures
  	and is generally not called directly by the website.
  
  	The RDF returned by this procedure is not equivalent to what is
  	returned by SPARQL. This procedure applies security rules, expands
  	nodes as defined by [Ontology.].[RDFExpand], and calculates network
  	information on-the-fly.
  
  	*/
  
  	declare @d datetime
  
  	declare @baseURI nvarchar(400)
  	select @baseURI = value from [Framework.].Parameter where ParameterID = 'baseURI'
  
  	select @subject = null where @subject = 0
  	select @predicate = null where @predicate = 0
  	select @object = null where @object = 0
  		
  	declare @firstURI nvarchar(400)
  	select @firstURI = @baseURI+cast(@subject as varchar(50))
  
  	declare @firstValue nvarchar(400)
  	select @firstValue = null
  	
  	declare @typeID bigint
  	select @typeID = [RDF.].fnURI2NodeID('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
  
  	declare @labelID bigint
  	select @labelID = [RDF.].fnURI2NodeID('http://www.w3.org/2000/01/rdf-schema#label')	
  
  	declare @validURI bit
  	select @validURI = 1
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Define temp tables
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	/*
  		drop table #subjects
  		drop table #types
  		drop table #expand
  		drop table #properties
  		drop table #connections
  	*/
  
  	create table #subjects (
  		subject bigint primary key,
  		showDetail bit,
  		expanded bit,
  		uri nvarchar(400)
  	)
  	
  	create table #types (
  		subject bigint not null,
  		object bigint not null,
  		predicate bigint,
  		showDetail bit,
  		uri nvarchar(400)
  	)
  	create unique clustered index idx_sop on #types (subject,object,predicate)
  
  	create table #expand (
  		subject bigint not null,
  		predicate bigint not null,
  		uri nvarchar(400),
  		property nvarchar(400),
  		tagName nvarchar(1000),
  		propertyLabel nvarchar(400),
  		IsDetail bit,
  		limit bigint,
  		showStats bit,
  		showSummary bit
  	)
  	alter table #expand add primary key (subject,predicate)
  
  	create table #properties (
  		uri nvarchar(400),
  		subject bigint,
  		predicate bigint,
  		object bigint,
  		showSummary bit,
  		property nvarchar(400),
  		tagName nvarchar(1000),
  		propertyLabel nvarchar(400),
  		Language nvarchar(255),
  		DataType nvarchar(255),
  		Value nvarchar(max),
  		ObjectType bit,
  		SortOrder int
  	)
  
  	create table #connections (
  		subject bigint,
  		subjectURI nvarchar(400),
  		predicate bigint,
  		predicateURI nvarchar(400),
  		object bigint,
  		Language nvarchar(255),
  		DataType nvarchar(255),
  		Value nvarchar(max),
  		ObjectType bit,
  		SortOrder int,
  		Weight float,
  		Reitification bigint,
  		ReitificationURI nvarchar(400),
  		connectionURI nvarchar(400)
  	)
  	
  	create table #ClassPropertyCustom (
  		ClassPropertyID int primary key,
  		IncludeProperty bit,
  		Limit int,
  		IncludeNetwork bit,
  		IncludeDescription bit
  	)
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Setup variables used for security
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	DECLARE @SecurityGroupID BIGINT, @HasSpecialViewAccess BIT, @HasSecurityGroupNodes BIT
  	EXEC [RDF.Security].GetSessionSecurityGroup @SessionID, @SecurityGroupID OUTPUT, @HasSpecialViewAccess OUTPUT
  	CREATE TABLE #SecurityGroupNodes (SecurityGroupNode BIGINT PRIMARY KEY)
  	INSERT INTO #SecurityGroupNodes (SecurityGroupNode) EXEC [RDF.Security].GetSessionSecurityGroupNodes @SessionID, @Subject
  	SELECT @HasSecurityGroupNodes = (CASE WHEN EXISTS (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END)
  
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Check if user has access to the URI
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	if @subject is not null
  		select @validURI = 0
  			where not exists (
  				select *
  				from [RDF.].Node
  				where NodeID = @subject
  					and ( (ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)) )
  			)
  
  	if @predicate is not null
  		select @validURI = 0
  			where not exists (
  				select *
  				from [RDF.].Node
  				where NodeID = @predicate and ObjectType = 0
  					and ( (ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)) )
  			)
  
  	if @object is not null
  		select @validURI = 0
  			where not exists (
  				select *
  				from [RDF.].Node
  				where NodeID = @object and ObjectType = 0
  					and ( (ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)) )
  			)
  
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Get subject information when it is a literal
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	select @dataStr = Value, @dataStrDataType = DataType, @dataStrLanguage = Language
  		from [RDF.].Node
  		where NodeID = @subject and ObjectType = 1
  			and ( (ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)) )
  
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Seed temp tables
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	---------------------------------------------------------------------------------------------
  	-- Profile [seed with the subject(s)]
  	---------------------------------------------------------------------------------------------
  	if (@subject is not null) and (@predicate is null) and (@object is null)
  	begin
  		insert into #subjects(subject,showDetail,expanded,URI)
  			select NodeID, @showDetails, 0, Value
  				from [RDF.].Node
  				where NodeID = @subject
  					and ((ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  		select @firstValue = URI
  			from #subjects s, [RDF.].Node n
  			where s.subject = @subject
  				and s.subject = n.NodeID and n.ObjectType = 0
  	end
  	if (@NodeListXML is not null)
  	begin
  		insert into #subjects(subject,showDetail,expanded,URI)
  			select n.NodeID, t.ShowDetails, 0, n.Value
  			from [RDF.].Node n, (
  				select NodeID, MAX(ShowDetails) ShowDetails
  				from (
  					select x.value('@ID','bigint') NodeID, IsNull(x.value('@ShowDetails','tinyint'),0) ShowDetails
  					from @NodeListXML.nodes('//Node') as N(x)
  				) t
  				group by NodeID
  				having NodeID not in (select subject from #subjects)
  			) t
  			where n.NodeID = t.NodeID and n.ObjectType = 0
  	end
  	
  	---------------------------------------------------------------------------------------------
  	-- Get all connections
  	---------------------------------------------------------------------------------------------
  	insert into #connections (subject, subjectURI, predicate, predicateURI, object, Language, DataType, Value, ObjectType, SortOrder, Weight, Reitification, ReitificationURI, connectionURI)
  		select	s.NodeID subject, s.value subjectURI, 
  				p.NodeID predicate, p.value predicateURI,
  				t.object, o.Language, o.DataType, o.Value, o.ObjectType,
  				t.SortOrder, t.Weight, 
  				r.NodeID Reitification, r.Value ReitificationURI,
  				@baseURI+cast(@subject as varchar(50))+'/'+cast(@predicate as varchar(50))+'/'+cast(object as varchar(50)) connectionURI
  			from [RDF.].Triple t
  				inner join [RDF.].Node s
  					on t.subject = s.NodeID
  				inner join [RDF.].Node p
  					on t.predicate = p.NodeID
  				inner join [RDF.].Node o
  					on t.object = o.NodeID
  				left join [RDF.].Node r
  					on t.reitification = r.NodeID
  						and t.reitification is not null
  						and ((r.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (r.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (r.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  			where @subject is not null and @predicate is not null
  				and s.NodeID = @subject 
  				and p.NodeID = @predicate 
  				and o.NodeID = IsNull(@object,o.NodeID)
  				and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  				and ((s.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (s.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (s.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  				and ((p.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (p.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (p.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  				and ((o.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (o.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (o.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  
  	-- Make sure there are connections
  	if (@subject is not null) and (@predicate is not null)
  		select @validURI = 0
  		where not exists (select * from #connections)
  
  	---------------------------------------------------------------------------------------------
  	-- Network [seed with network statistics and connections]
  	---------------------------------------------------------------------------------------------
  	if (@subject is not null) and (@predicate is not null) and (@object is null)
  	begin
  		select @firstURI = @baseURI+cast(@subject as varchar(50))+'/'+cast(@predicate as varchar(50))
  		-- Basic network properties
  		;with networkProperties as (
  			select 1 n, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' property, 'rdf:type' tagName, 'type' propertyLabel, 0 ObjectType
  			union all select 2, 'http://profiles.catalyst.harvard.edu/ontology/prns#numberOfConnections', 'prns:numberOfConnections', 'number of connections', 1
  			union all select 3, 'http://profiles.catalyst.harvard.edu/ontology/prns#maxWeight', 'prns:maxWeight', 'maximum connection weight', 1
  			union all select 4, 'http://profiles.catalyst.harvard.edu/ontology/prns#minWeight', 'prns:minWeight', 'minimum connection weight', 1
  			union all select 5, 'http://profiles.catalyst.harvard.edu/ontology/prns#predicateNode', 'prns:predicateNode', 'predicate node', 0
  			union all select 6, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate', 'rdf:predicate', 'predicate', 0
  			union all select 7, 'http://www.w3.org/2000/01/rdf-schema#label', 'rdfs:label', 'label', 1
  			union all select 8, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#subject', 'rdf:subject', 'subject', 0
  		), networkStats as (
  			select	cast(isnull(count(*),0) as varchar(50)) numberOfConnections,
  					cast(isnull(max(Weight),1) as varchar(50)) maxWeight,
  					cast(isnull(min(Weight),1) as varchar(50)) minWeight,
  					max(predicateURI) predicateURI
  				from #connections
  		), subjectLabel as (
  			select IsNull(Max(o.Value),'') Label
  			from [RDF.].Triple t, [RDF.].Node o
  			where t.subject = @subject
  				and t.predicate = @labelID
  				and t.object = o.NodeID
  				and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  				and ((o.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (o.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (o.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  		)
  		insert into #properties (uri,predicate,property,tagName,propertyLabel,Value,ObjectType,SortOrder)
  			select	@firstURI,
  					[RDF.].fnURI2NodeID(p.property), p.property, p.tagName, p.propertyLabel,
  					(case p.n when 1 then 'http://profiles.catalyst.harvard.edu/ontology/prns#Network'
  								when 2 then n.numberOfConnections
  								when 3 then n.maxWeight
  								when 4 then n.minWeight
  								when 5 then @baseURI+cast(@predicate as varchar(50))
  								when 6 then n.predicateURI
  								when 7 then l.Label
  								when 8 then @baseURI+cast(@subject as varchar(50))
  								end),
  					p.ObjectType,
  					1
  				from networkStats n, networkProperties p, subjectLabel l
  		-- Limit the number of connections if the subject is not a person
  		select @limit = 10
  			where (@limit is null) 
  				and not exists (
  					select *
  					from [rdf.].[triple]
  					where subject = @subject
  						and predicate = [RDF.].fnURI2NodeID('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
  						and object = [RDF.].fnURI2NodeID('http://xmlns.com/foaf/0.1/Person')
  				)
  		-- Remove connections not within offset-limit window
  		delete from #connections
  			where (SortOrder < 1+IsNull(@offset,0)) or (SortOrder > IsNull(@limit,SortOrder) + (case when IsNull(@offset,0)<1 then 0 else @offset end))
  		-- Add hasConnection properties
  		insert into #properties (uri,predicate,property,tagName,propertyLabel,Value,ObjectType,SortOrder)
  			select	@baseURI+cast(@subject as varchar(50))+'/'+cast(@predicate as varchar(50)),
  					[RDF.].fnURI2NodeID('http://profiles.catalyst.harvard.edu/ontology/prns#hasConnection'), 
  					'http://profiles.catalyst.harvard.edu/ontology/prns#hasConnection', 'prns:hasConnection', 'has connection',
  					connectionURI,
  					0,
  					SortOrder
  				from #connections
  	end
  
  	---------------------------------------------------------------------------------------------
  	-- Connection [seed with connection]
  	---------------------------------------------------------------------------------------------
  	if (@subject is not null) and (@predicate is not null) and (@object is not null)
  	begin
  		select @firstURI = @baseURI+cast(@subject as varchar(50))+'/'+cast(@predicate as varchar(50))+'/'+cast(@object as varchar(50))
  	end
  
  	---------------------------------------------------------------------------------------------
  	-- Expanded Connections [seed with statistics, subject, object, and connectionDetails]
  	---------------------------------------------------------------------------------------------
  	if (@expand = 1 or @object is not null) and exists (select * from #connections)
  	begin
  		-- Connection statistics
  		;with connectionProperties as (
  			select 1 n, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' property, 'rdf:type' tagName, 'type' propertyLabel, 0 ObjectType
  			union all select 2, 'http://profiles.catalyst.harvard.edu/ontology/prns#connectionWeight', 'prns:connectionWeight', 'connection weight', 1
  			union all select 3, 'http://profiles.catalyst.harvard.edu/ontology/prns#sortOrder', 'prns:sortOrder', 'sort order', 1
  			union all select 4, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#object', 'rdf:object', 'object', 0
  			union all select 5, 'http://profiles.catalyst.harvard.edu/ontology/prns#hasConnectionDetails', 'prns:hasConnectionDetails', 'connection details', 0
  			union all select 6, 'http://profiles.catalyst.harvard.edu/ontology/prns#predicateNode', 'prns:predicateNode', 'predicate node', 0
  			union all select 7, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate', 'rdf:predicate', 'predicate', 0
  			union all select 8, 'http://www.w3.org/2000/01/rdf-schema#label', 'rdfs:label', 'label', 1
  			union all select 9, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#subject', 'rdf:subject', 'subject', 0
  			union all select 10, 'http://profiles.catalyst.harvard.edu/ontology/prns#connectionInNetwork', 'prns:connectionInNetwork', 'connection in network', 0
  		)
  		insert into #properties (uri,predicate,property,tagName,propertyLabel,Value,ObjectType,SortOrder)
  			select	connectionURI,
  					[RDF.].fnURI2NodeID(p.property), p.property, p.tagName, p.propertyLabel,
  					(case p.n	when 1 then 'http://profiles.catalyst.harvard.edu/ontology/prns#Connection'
  								when 2 then cast(c.Weight as varchar(50))
  								when 3 then cast(c.SortOrder as varchar(50))
  								when 4 then c.value
  								when 5 then c.ReitificationURI
  								when 6 then @baseURI+cast(@predicate as varchar(50))
  								when 7 then c.predicateURI
  								when 8 then l.value
  								when 9 then c.subjectURI
  								when 10 then c.subjectURI+'/'+cast(@predicate as varchar(50))
  								end),
  					(case p.n when 4 then c.ObjectType else p.ObjectType end),
  					1
  				from #connections c, connectionProperties p
  					left outer join (
  						select o.value
  							from [RDF.].Triple t, [RDF.].Node o
  							where t.subject = @subject 
  								and t.predicate = @labelID
  								and t.object = o.NodeID
  								and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  								and ((o.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (o.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (o.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes)))
  					) l on p.n = 8
  				where (p.n < 5) 
  					or (p.n = 5 and c.ReitificationURI is not null)
  					or (p.n > 5 and @object is not null)
  		if (@expand = 1)
  		begin
  			-- Connection subject
  			insert into #subjects (subject, showDetail, expanded, URI)
  				select NodeID, 0, 0, Value
  					from [RDF.].Node
  					where NodeID = @subject
  			-- Connection objects
  			insert into #subjects (subject, showDetail, expanded, URI)
  				select object, 0, 0, value
  					from #connections
  					where ObjectType = 0 and object not in (select subject from #subjects)
  			-- Connection details (reitifications)
  			insert into #subjects (subject, showDetail, expanded, URI)
  				select Reitification, 0, 0, ReitificationURI
  					from #connections
  					where Reitification is not null and Reitification not in (select subject from #subjects)
  		end
  	end
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Get property values
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	-- Get custom settings to override the [Ontology.].[ClassProperty] default values
  	insert into #ClassPropertyCustom (ClassPropertyID, IncludeProperty, Limit, IncludeNetwork, IncludeDescription)
  		select p.ClassPropertyID, t.IncludeProperty, t.Limit, t.IncludeNetwork, t.IncludeDescription
  			from [Ontology.].[ClassProperty] p
  				inner join (
  					select	x.value('@Class','varchar(400)') Class,
  							x.value('@NetworkProperty','varchar(400)') NetworkProperty,
  							x.value('@Property','varchar(400)') Property,
  							(case x.value('@IncludeProperty','varchar(5)') when 'true' then 1 when 'false' then 0 else null end) IncludeProperty,
  							x.value('@Limit','int') Limit,
  							(case x.value('@IncludeNetwork','varchar(5)') when 'true' then 1 when 'false' then 0 else null end) IncludeNetwork,
  							(case x.value('@IncludeDescription','varchar(5)') when 'true' then 1 when 'false' then 0 else null end) IncludeDescription
  					from @ExpandRDFListXML.nodes('//ExpandRDF') as R(x)
  				) t
  				on p.Class=t.Class and p.Property=t.Property
  					and ((p.NetworkProperty is null and t.NetworkProperty is null) or (p.NetworkProperty = t.NetworkProperty))
  
  	-- Get properties and loop if objects need to be expanded
  	declare @numLoops int
  	declare @maxLoops int
  	declare @actualLoops int
  	declare @NewSubjects int
  	select @numLoops = 0, @maxLoops = 10, @actualLoops = 0
  	while (@numLoops < @maxLoops)
  	begin
  		-- Get the types of each subject that hasn't been expanded
  		truncate table #types
  		insert into #types(subject,object,predicate,showDetail,uri)
  			select s.subject, t.object, null, s.showDetail, s.uri
  				from #subjects s 
  					inner join [RDF.].Triple t on s.subject = t.subject 
  						and t.predicate = @typeID 
  					inner join [RDF.].Node n on t.object = n.NodeID
  						and ((n.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (n.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN n.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  						and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  				where s.expanded = 0				   
  		-- Get the subject types of each reitification that hasn't been expanded
  		insert into #types(subject,object,predicate,showDetail,uri)
  		select distinct s.subject, t.object, r.predicate, s.showDetail, s.uri
  			from #subjects s 
  				inner join [RDF.].Triple r on s.subject = r.reitification
  				inner join [RDF.].Triple t on r.subject = t.subject 
  					and t.predicate = @typeID 
  				inner join [RDF.].Node n on t.object = n.NodeID
  					and ((n.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (n.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN n.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  					and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  					and ((r.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (r.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN r.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  			where s.expanded = 0
  		-- Get the items that should be expanded
  		truncate table #expand
  		insert into #expand(subject, predicate, uri, property, tagName, propertyLabel, IsDetail, limit, showStats, showSummary)
  			select p.subject, o._PropertyNode, max(p.uri) uri, o.property, o._TagName, o._PropertyLabel, min(o.IsDetail*1) IsDetail, 
  					(case when min(o.IsDetail*1) = 0 then max(case when o.IsDetail=0 then IsNull(c.limit,o.limit) else null end) else max(IsNull(c.limit,o.limit)) end) limit,
  					(case when min(o.IsDetail*1) = 0 then max(case when o.IsDetail=0 then IsNull(c.IncludeNetwork,o.IncludeNetwork)*1 else 0 end) else max(IsNull(c.IncludeNetwork,o.IncludeNetwork)*1) end) showStats,
  					(case when min(o.IsDetail*1) = 0 then max(case when o.IsDetail=0 then IsNull(c.IncludeDescription,o.IncludeDescription)*1 else 0 end) else max(IsNull(c.IncludeDescription,o.IncludeDescription)*1) end) showSummary
  				from #types p
  					inner join [Ontology.].ClassProperty o
  						on p.object = o._ClassNode 
  						and ((p.predicate is null and o._NetworkPropertyNode is null) or (p.predicate = o._NetworkPropertyNode))
  						and o.IsDetail <= p.showDetail
  					left outer join #ClassPropertyCustom c
  						on o.ClassPropertyID = c.ClassPropertyID
  				where IsNull(c.IncludeProperty,1) = 1
  				group by p.subject, o.property, o._PropertyNode, o._TagName, o._PropertyLabel
  		-- Get the values for each property that should be expanded
  		insert into #properties (uri,subject,predicate,object,showSummary,property,tagName,propertyLabel,Language,DataType,Value,ObjectType,SortOrder)
  			select e.uri, e.subject, t.predicate, t.object, e.showSummary,
  					e.property, e.tagName, e.propertyLabel, 
  					o.Language, o.DataType, o.Value, o.ObjectType, t.SortOrder
  			from #expand e
  				inner join [RDF.].Triple t
  					on t.subject = e.subject and t.predicate = e.predicate
  						and (e.limit is null or t.sortorder <= e.limit)
  						and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  				inner join [RDF.].Node p
  					on t.predicate = p.NodeID
  						and ((p.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (p.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN p.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  				inner join [RDF.].Node o
  					on t.object = o.NodeID
  						and ((o.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (o.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN o.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  		-- Get network properties
  		if (@numLoops = 0)
  		begin
  			-- Calculate network statistics
  			select e.uri, e.subject, t.predicate, e.property, e.tagName, e.PropertyLabel, 
  					cast(isnull(count(*),0) as varchar(50)) numberOfConnections,
  					cast(isnull(max(t.Weight),1) as varchar(50)) maxWeight,
  					cast(isnull(min(t.Weight),1) as varchar(50)) minWeight,
  					@baseURI+cast(e.subject as varchar(50))+'/'+cast(t.predicate as varchar(50)) networkURI
  				into #networks
  				from #expand e
  					inner join [RDF.].Triple t
  						on t.subject = e.subject and t.predicate = e.predicate
  							and (e.showStats = 1)
  							and ((t.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (t.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN t.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  					inner join [RDF.].Node p
  						on t.predicate = p.NodeID
  							and ((p.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (p.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN p.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  					inner join [RDF.].Node o
  						on t.object = o.NodeID
  							and ((o.ViewSecurityGroup BETWEEN @SecurityGroupID AND -1) OR (o.ViewSecurityGroup > 0 AND @HasSpecialViewAccess = 1) OR (1 = CASE WHEN @HasSecurityGroupNodes = 0 THEN 0 WHEN o.ViewSecurityGroup IN (SELECT * FROM #SecurityGroupNodes) THEN 1 ELSE 0 END))
  				group by e.uri, e.subject, t.predicate, e.property, e.tagName, e.PropertyLabel
  			-- Create properties from network statistics
  			;with networkProperties as (
  				select 1 n, 'http://profiles.catalyst.harvard.edu/ontology/prns#hasNetwork' property, 'prns:hasNetwork' tagName, 'has network' propertyLabel, 0 ObjectType
  				union all select 2, 'http://profiles.catalyst.harvard.edu/ontology/prns#numberOfConnections', 'prns:numberOfConnections', 'number of connections', 1
  				union all select 3, 'http://profiles.catalyst.harvard.edu/ontology/prns#maxWeight', 'prns:maxWeight', 'maximum connection weight', 1
  				union all select 4, 'http://profiles.catalyst.harvard.edu/ontology/prns#minWeight', 'prns:minWeight', 'minimum connection weight', 1
  				union all select 5, 'http://profiles.catalyst.harvard.edu/ontology/prns#predicateNode', 'prns:predicateNode', 'predicate node', 0
  				union all select 6, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate', 'rdf:predicate', 'predicate', 0
  				union all select 7, 'http://www.w3.org/2000/01/rdf-schema#label', 'rdfs:label', 'label', 1
  				union all select 8, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'rdf:type', 'type', 0
  			)
  			insert into #properties (uri,subject,predicate,property,tagName,propertyLabel,Value,ObjectType,SortOrder)
  				select	(case p.n when 1 then n.uri else n.networkURI end),
  						(case p.n when 1 then subject else null end),
  						[RDF.].fnURI2NodeID(p.property), p.property, p.tagName, p.propertyLabel,
  						(case p.n when 1 then n.networkURI 
  									when 2 then n.numberOfConnections
  									when 3 then n.maxWeight
  									when 4 then n.minWeight
  									when 5 then @baseURI+cast(n.predicate as varchar(50))
  									when 6 then n.property
  									when 7 then n.PropertyLabel
  									when 8 then 'http://profiles.catalyst.harvard.edu/ontology/prns#Network'
  									end),
  						p.ObjectType,
  						1
  					from #networks n, networkProperties p
  					where p.n = 1 or @expand = 1
  		end
  		-- Mark that all previous subjects have been expanded
  		update #subjects set expanded = 1 where expanded = 0
  		-- See if there are any new subjects that need to be expanded
  		insert into #subjects(subject,showDetail,expanded,uri)
  			select distinct object, 0, 0, value
  				from #properties
  				where showSummary = 1
  					and ObjectType = 0
  					and object not in (select subject from #subjects)
  		select @NewSubjects = @@ROWCOUNT		
  		insert into #subjects(subject,showDetail,expanded,uri)
  			select distinct predicate, 0, 0, property
  				from #properties
  				where predicate is not null
  					and predicate not in (select subject from #subjects)
  		-- If no subjects need to be expanded, then we are done
  		if @NewSubjects + @@ROWCOUNT = 0
  			select @numLoops = @maxLoops
  		select @numLoops = @numLoops + 1 + @maxLoops * (1 - @expand)
  		select @actualLoops = @actualLoops + 1
  	end
  	-- Add tagName as a property of DatatypeProperty and ObjectProperty classes
  	insert into #properties (uri, subject, showSummary, property, tagName, propertyLabel, Value, ObjectType, SortOrder)
  		select p.uri, p.subject, 0, 'http://profiles.catalyst.harvard.edu/ontology/prns#tagName', 'prns:tagName', 'tag name', 
  				n.prefix+':'+substring(p.uri,len(n.uri)+1,len(p.uri)), 1, 1
  			from #properties p, [Ontology.].Namespace n
  			where p.property = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
  				and p.value in ('http://www.w3.org/2002/07/owl#DatatypeProperty','http://www.w3.org/2002/07/owl#ObjectProperty')
  				and p.uri like n.uri+'%'
  	--select @actualLoops
  	--select * from #properties order by (case when uri = @firstURI then 0 else 1 end), uri, tagName, value
  
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Handle the special case where a local node is storing a copy of an external URI
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	if (@firstValue IS NOT NULL) AND (@firstValue <> @firstURI)
  		insert into #properties (uri, subject, predicate, object, 
  				showSummary, property, 
  				tagName, propertyLabel, 
  				Language, DataType, Value, ObjectType, SortOrder
  			)
  			select @firstURI uri, @subject subject, predicate, object, 
  					showSummary, property, 
  					tagName, propertyLabel, 
  					Language, DataType, Value, ObjectType, 1 SortOrder
  				from #properties
  				where uri = @firstValue
  					and not exists (select * from #properties where uri = @firstURI)
  			union all
  			select @firstURI uri, @subject subject, null predicate, null object, 
  					0 showSummary, 'http://www.w3.org/2002/07/owl#sameAs' property,
  					'owl:sameAs' tagName, 'same as' propertyLabel, 
  					null Language, null DataType, @firstValue Value, 0 ObjectType, 1 SortOrder
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Generate an XML string from the node properties table
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	declare @description nvarchar(max)
  	select @description = ''
  	-- sort the tags
  	select *, 
  			row_number() over (partition by uri order by i) j, 
  			row_number() over (partition by uri order by i desc) k 
  		into #propertiesSorted
  		from (
  			select *, row_number() over (order by (case when uri = @firstURI then 0 else 1 end), uri, tagName, SortOrder, value) i
  				from #properties
  		) t
  	create unique clustered index idx_i on #propertiesSorted(i)
  	-- handle special xml characters in the uri and value strings
  	update #propertiesSorted
  		set uri = replace(replace(replace(uri,'&','&amp;'),'<','&lt;'),'>','&gt;')
  		where uri like '%[&<>]%'
  	update #propertiesSorted
  		set value = replace(replace(replace(value,'&','&amp;'),'<','&lt;'),'>','&gt;')
  		where value like '%[&<>]%'
  	-- concatenate the tags
  	select @description = (
  			select (case when j=1 then '<rdf:Description rdf:about="' + uri + '">' else '' end)
  					+'<'+tagName
  					+(case when ObjectType = 0 then ' rdf:resource="'+value+'"/>' else '>'+value+'</'+tagName+'>' end)
  					+(case when k=1 then '</rdf:Description>' else '' end)
  			from #propertiesSorted
  			order by i
  			for xml path(''), type
  		).value('(./text())[1]','nvarchar(max)')
  	-- default description if none exists
  	if (@description IS NULL) OR (@validURI = 0)
  		select @description = '<rdf:Description rdf:about="' + @firstURI + '"'
  			+IsNull(' xml:lang="'+@dataStrLanguage+'"','')
  			+IsNull(' rdf:datatype="'+@dataStrDataType+'"','')
  			+IsNull(' >'+replace(replace(replace(@dataStr,'&','&amp;'),'<','&lt;'),'>','&gt;')+'</rdf:Description>',' />')
  
  
  	--*******************************************************************************************
  	--*******************************************************************************************
  	-- Return as a string or as XML
  	--*******************************************************************************************
  	--*******************************************************************************************
  
  	select @dataStr = IsNull(@dataStr,@description)
  
  	declare @x as varchar(max)
  	select @x = '<rdf:RDF'
  	select @x = @x + ' xmlns:'+Prefix+'="'+URI+'"' 
  		from [Ontology.].Namespace
  	select @x = @x + ' >' + @description + '</rdf:RDF>'
  
  	if @returnXML = 1 and @returnXMLasStr = 0
  		select cast(replace(@x,char(13),'&#13;') as xml) RDF
  
  	if @returnXML = 1 and @returnXMLasStr = 1
  		select @x RDF
  
  	/*	
  		declare @d datetime
  		select @d = getdate()
  		select datediff(ms,@d,getdate())
  	*/
  		
  END
  
  GO
  