/*******************************************************************************
 * Copyright 2012 University of Southern California
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * 	http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This code was developed by the Information Integration Group as part 
 * of the Karma project at the Information Sciences Institute of the 
 * University of Southern California.  For more information, publications, 
 * and related projects, please see: http://www.isi.edu/integration
 ******************************************************************************/

package edu.isi.karma.kr2rml.mapping;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;

import edu.isi.karma.kr2rml.KR2RMLVersion;
import edu.isi.karma.kr2rml.ObjectMap;
import edu.isi.karma.kr2rml.Predicate;
import edu.isi.karma.kr2rml.PredicateObjectMap;
import edu.isi.karma.kr2rml.Prefix;
import edu.isi.karma.kr2rml.RefObjectMap;
import edu.isi.karma.kr2rml.SubjectMap;
import edu.isi.karma.kr2rml.formatter.KR2RMLColumnNameFormatter;
import edu.isi.karma.kr2rml.formatter.KR2RMLColumnNameFormatterFactory;
import edu.isi.karma.kr2rml.planning.TriplesMap;
import edu.isi.karma.kr2rml.planning.TriplesMapLink;
import edu.isi.karma.kr2rml.template.ColumnTemplateTerm;
import edu.isi.karma.kr2rml.template.StringTemplateTerm;
import edu.isi.karma.kr2rml.template.TemplateTerm;
import edu.isi.karma.kr2rml.template.TemplateTermSet;
import edu.isi.karma.kr2rml.template.TemplateTermSetBuilder;
import edu.isi.karma.modeling.Uris;
import edu.isi.karma.rep.metadata.WorksheetProperties.SourceTypes;
import edu.isi.karma.webserver.KarmaException;

public class WorksheetR2RMLJenaModelParser {
	
	private Model model;
	private R2RMLMappingIdentifier id;
	private KR2RMLMapping mapping;
	private static Logger logger = LoggerFactory.getLogger(WorksheetR2RMLJenaModelParser.class);
	
	
	public WorksheetR2RMLJenaModelParser(R2RMLMappingIdentifier id) throws JSONException, KarmaException 
	{
		this.id = id;
	}
	
	public WorksheetR2RMLJenaModelParser(Model model, R2RMLMappingIdentifier id) throws JSONException, KarmaException 
	{
		this.id = id;
	}
	
	public synchronized Model getModel() throws IOException {
		if(this.model == null){
			loadModel();
		}
		return model;
	}
	
	public Model getModelFromCache(){
		return model;
	}

	private void loadModel() throws IOException {
		if (model != null) {
			return;
		}
		synchronized(this)
		{	
			if(model == null)
			{
				this.model = loadSourceModelIntoJenaModel(id);
			}
		}
	}

	public KR2RMLMapping parse() throws IOException, KarmaException, JSONException
	{
		loadModel();
		
		if(null != mapping)
		{
			return mapping;
		}
		synchronized(this)
		{
			if(null != mapping)
			{
				return mapping;
			}
		// Capture the main mapping resource that corresponds to the source name
		Resource mappingResource = getMappingResourceFromSourceName();
		if (mappingResource == null) {
			throw new KarmaException("Resource not found in model for the source: " + id.getName());
		}
		
		Property modelVersionNameProp = model.getProperty(Uris.KM_MODEL_VERSION_URI);
		Statement s = model.getProperty(mappingResource, modelVersionNameProp);
		
		KR2RMLVersion version;
		try 
		{
			version = new KR2RMLVersion(s.getString());
		}
		catch (Exception e)
		{
			version = KR2RMLVersion.unknown;
		}
		KR2RMLMapping kr2rmlMapping = new KR2RMLMapping(id, version);
		Map<String, String> prefixes = model.getNsPrefixMap();
		for(Entry<String, String> prefix : prefixes.entrySet())
		{
			Prefix p = new Prefix(prefix.getKey(), prefix.getValue());
			kr2rmlMapping.addPrefix(p);
		}
		
		SourceTypes sourceType = getSourceType(mappingResource);
		kr2rmlMapping.setColumnNameFormatter(KR2RMLColumnNameFormatterFactory.getFormatter(sourceType));
		// Load any transformations on the worksheet if required
		loadWorksheetHistory(mappingResource, kr2rmlMapping);
		
		// Generate TriplesMap for each InternalNode in the tree
		List<Resource> subjectResources = createSubjectMaps(mappingResource, kr2rmlMapping);
		
		// Identify the object property links
		createPredicateObjectMaps(mappingResource, kr2rmlMapping);
		
		// Calculate the nodes covered by each InternalNode
		calculateColumnNodesCoveredByBlankNodes(kr2rmlMapping, subjectResources);
		createGraphNodeToTriplesNodeMap(kr2rmlMapping);
		return mapping = kr2rmlMapping;
		}
	}
	
	
	private void createGraphNodeToTriplesNodeMap(KR2RMLMapping kr2rmlMapping) throws FileNotFoundException, UnsupportedEncodingException{
		
		StmtIterator itr = model.listStatements(null, model.getProperty(Uris.KM_NODE_ID_URI), (RDFNode)null);
		Resource subject;
		Map<String,String> graphNodeIdToTriplesMapIdMap = kr2rmlMapping.getAuxInfo().getGraphNodeIdToTriplesMapIdMap();
		while (itr.hasNext()) {
			Statement subjMapToNodeIdStmt = itr.next();
			String nodeId = subjMapToNodeIdStmt.getObject().toString();
			subject = subjMapToNodeIdStmt.getSubject();
			if (subject != null) {
				StmtIterator itr2 = model.listStatements(null, model.getProperty(Uris.RR_SUBJECTMAP_URI), subject);
				while (itr2.hasNext()) {
					String triplesMapId = itr2.next().getSubject().toString();
					graphNodeIdToTriplesMapIdMap.put(nodeId, triplesMapId);
				}
				
			}
		}
	}
	
	private SourceTypes getSourceType(Resource mappingResource)
	{
		Property sourceNameProp = model.getProperty(Uris.KM_SOURCE_TYPE_URI);
		Statement s = model.getProperty(mappingResource, sourceNameProp);
		String sourceType;
		if(s != null)
		{
			RDFNode node = s.getObject();
			if(node != null && node.isLiteral())
			{
				sourceType = node.asLiteral().getString();
				return SourceTypes.valueOf(sourceType);
			}
		}
		return SourceTypes.CSV;
		
		
	}
    
	public static Model loadSourceModelIntoJenaModel(R2RMLMappingIdentifier id) throws IOException {
        // Create an empty Model
        Model model = ModelFactory.createDefaultModel();
        InputStream s;
        if(id.getContent() != null) {
        	s = IOUtils.toInputStream(id.getContent());
        } else {
        	URL modelURL = id.getLocation();
        	logger.info("Load model:" + modelURL.toString());
        	s = modelURL.openStream();
        }
        model.read(s, null, "TURTLE");
        return model;
    }
   
	private Resource getMappingResourceFromSourceName() throws KarmaException {
		Property sourceNameProp = model.getProperty(Uris.KM_SOURCE_NAME_URI);
		RDFNode node = model.createLiteral(id.getName());
		ResIterator res = model.listResourcesWithProperty(sourceNameProp, node);
		List<Resource> resList = res.toList();
		
		if (resList.size() > 1) {
			throw new KarmaException("More than one resource exists with source name: " + id.getName());
		} else if (resList.size() == 1) {
			return resList.get(0);
		} else {
			//If we didnt find the sourceName in the model, maybe it is a different source with the
			//same schema.
			//Maybe we need to substitute the sourceName in the model with this one
			NodeIterator sourceObjectIter = model.listObjectsOfProperty(sourceNameProp);
			List<RDFNode> sourceObjects = sourceObjectIter.toList();
			
			if(sourceObjects.size() > 1) {
				throw new KarmaException("More than one resource exists with source name: " + id.getName());
			} else if(sourceObjects.size() == 1) {
				RDFNode prevSourceObject = sourceObjects.get(0);
				
				//We got the previous source object, now get the Subject Node for this
				ResIterator prevSourceSubjectsIter = model.listResourcesWithProperty(sourceNameProp, prevSourceObject);
				List<Resource> prevSourceSubjects = prevSourceSubjectsIter.toList();
				
				if (prevSourceSubjects.size() == 1) {
					Resource subject = prevSourceSubjects.get(0);
					model.remove(subject, sourceNameProp, prevSourceObject);
					model.add(subject, sourceNameProp, node);
					return subject;
				} else if(prevSourceSubjects.size() > 1) {
					throw new KarmaException("More than one resource exists with model source name: " + prevSourceObject.toString());
				}
			}
			return null;
		}
	}
	
	private void loadWorksheetHistory(Resource mappingResource, KR2RMLMapping kr2rmlMapping) throws JSONException {
		JSONArray normalizedCommandsJSON = getWorksheetHistory(mappingResource);
		kr2rmlMapping.setWorksheetHistory(normalizedCommandsJSON);
	}

	private JSONArray getWorksheetHistory(Resource mappingResource) throws JSONException {
		Property hasTransformation = model.getProperty(Uris.KM_HAS_WORKSHEET_HISTORY_URI);
		NodeIterator transItr = model.listObjectsOfProperty(mappingResource, hasTransformation);
		while (transItr.hasNext()) {
			String commands = transItr.next().toString();
			return new JSONArray(commands);
		}
		return new JSONArray();
	}

	private void createPredicateObjectMaps(Resource mappingResource, KR2RMLMapping kr2rmlMapping) throws JSONException {
		Property hasTrMapUri = model.getProperty(Uris.KM_HAS_TRIPLES_MAP_URI);
		
		// Get all the triple maps
		NodeIterator trMapsResItr = model.listObjectsOfProperty(mappingResource, hasTrMapUri);
		while (trMapsResItr.hasNext()) {
			// Add the predicate object maps
			addPredicateObjectMapsForTripleMap(trMapsResItr.next().asResource(), kr2rmlMapping);
		}
	}

	private List<Resource> createSubjectMaps(Resource mappingResource, KR2RMLMapping kr2rmlMapping) throws JSONException {
		List<Resource> subjectMapResources = new ArrayList<>();
		Property hasTrMapUri = model.getProperty(Uris.KM_HAS_TRIPLES_MAP_URI);
		
		// Get all the triple maps
		NodeIterator trMapsResItr = model.listObjectsOfProperty(mappingResource, hasTrMapUri);
		while (trMapsResItr.hasNext()) {
			Resource trMapRes = trMapsResItr.next().asResource();
			SubjectMap subjMap = addSubjectMapForTripleMap(trMapRes, kr2rmlMapping, subjectMapResources);
			
			// Add the Triples map
			TriplesMap trMap = new TriplesMap(trMapRes.getURI(), subjMap);
			kr2rmlMapping.getTriplesMapIndex().put(trMapRes.getURI(), trMap);
			kr2rmlMapping.addTriplesMap(trMap);
			kr2rmlMapping.getAuxInfo().getTriplesMapGraph().addTriplesMap(trMap);
		}
		return subjectMapResources;
	}


	private void addPredicateObjectMapsForTripleMap(Resource trMapRes, KR2RMLMapping kr2rmlMapping) throws  JSONException {
		int predicateIdCounter = 0;
		int objectMapCounter = 0;
		Property predObjMapProp = model.getProperty(Uris.RR_PRED_OBJ_MAP_URI);
		Property predProp = model.getProperty(Uris.RR_PREDICATE_URI);
		Property objectMapProp = model.getProperty(Uris.RR_OBJECTMAP_URI);
		Property columnProp = model.getProperty(Uris.RR_COLUMN_URI);
		Property rdfLiteralTypeProp = model.getProperty(Uris.RR_DATATYPE_URI);
		Resource rfObjClassUri = model.getResource(Uris.RR_REF_OBJECT_MAP_CLASS_URI);
		Property parentTriplesMapProp = model.getProperty(Uris.RR_PARENT_TRIPLE_MAP_URI);
		Property rdfTypeProp = model.getProperty(Uris.RDF_TYPE_URI);
		Property templateProp = model.getProperty(Uris.RR_TEMPLATE_URI);
		Property constantProp = model.getProperty(Uris.RR_CONSTANT);
		Property languageProp = model.getProperty(Uris.RR_LANGUAGE_URI);
		KR2RMLColumnNameFormatter formatter = kr2rmlMapping.getColumnNameFormatter();
		TriplesMap trMap = kr2rmlMapping.getTriplesMapIndex().get(trMapRes.getURI());
		if (trMap == null) {
			logger.error("No Triples Map found for resource: " + trMapRes.getURI());
			return;
		}
		NodeIterator predObjItr = model.listObjectsOfProperty(trMapRes, predObjMapProp);
		while (predObjItr.hasNext()) {
			Resource pomBlankNode = predObjItr.next().asResource();
			// Create the PredicateObjectMap object for current POM
			PredicateObjectMap pom = new PredicateObjectMap(pomBlankNode.getURI(), trMap);
			
			// Get the predicate for the POM
			Predicate pred = null;
			NodeIterator pomPredItr = model.listObjectsOfProperty(pomBlankNode, predProp); 
			while (pomPredItr.hasNext()) {
				RDFNode pomPredNode = pomPredItr.next();
				pred = new Predicate(pomPredNode.toString() + "-" + predicateIdCounter++);
				
				// Check if the predicate value is a URI or a literal (such as column name)
				if (pomPredNode instanceof Resource) {
					pred.getTemplate().addTemplateTermToSet(
							new StringTemplateTerm(((Resource) pomPredNode).getURI(), true));
				} else {
					pred.setTemplate(TemplateTermSetBuilder.
							constructTemplateTermSetFromR2rmlTemplateString(
									pomPredNode.toString(), formatter));
				}
			}
			pom.setPredicate(pred);
			
			// Get the object for the POM
			ObjectMap objMap = null;
			NodeIterator pomObjItr = model.listObjectsOfProperty(pomBlankNode, objectMapProp);
			
			while (pomObjItr.hasNext()) {
				Resource objNode = pomObjItr.next().asResource();
				
				/** Check if objBlankNode is a RefObjectMap or a normal object map with column **/
				if (model.contains(objNode, rdfTypeProp, rfObjClassUri)) {
					NodeIterator parentTripleMapItr = model.listObjectsOfProperty(objNode, 
							parentTriplesMapProp);
					while (parentTripleMapItr.hasNext()) {
						Resource parentTripleRes = parentTripleMapItr.next().asResource();
						TriplesMap parentTM = kr2rmlMapping.getTriplesMapIndex().get(parentTripleRes.getURI());
						
						// Create a RefObjectMap
						RefObjectMap rfMap = new RefObjectMap(objNode.getURI(), parentTM);
						objMap = new ObjectMap(getNewObjectMapId(objectMapCounter++), rfMap);
						
						// Add the link between triple maps in the auxInfo
						TriplesMapLink link = new TriplesMapLink(trMap, parentTM, pom);  
						kr2rmlMapping.getAuxInfo().getTriplesMapGraph().addLink(link);
					}
				} else {
					
					// RDF Literal Type
					Statement objMapRdfLiteralTypeStmt = model.getProperty(objNode, rdfLiteralTypeProp);
					TemplateTermSet rdfLiteralTypeTermSet = null;
					if (objMapRdfLiteralTypeStmt != null && objMapRdfLiteralTypeStmt.getObject().isLiteral()) {
						StringTemplateTerm rdfLiteralTypeTerm = 
								new StringTemplateTerm(objMapRdfLiteralTypeStmt.getObject().toString(), true);
						rdfLiteralTypeTermSet = new TemplateTermSet();
						rdfLiteralTypeTermSet.addTemplateTermToSet(rdfLiteralTypeTerm);
					}
					NodeIterator objMapColLanguageStmts = model.listObjectsOfProperty(objNode, languageProp);
					TemplateTermSet objMapLangTermSet = null;
					while (objMapColLanguageStmts.hasNext()) {
						RDFNode languageNode = objMapColLanguageStmts.next();
						StringTemplateTerm languageTerm = 
								new StringTemplateTerm(languageNode.toString());
						objMapLangTermSet = new TemplateTermSet();
						objMapLangTermSet.addTemplateTermToSet(languageTerm);
						
					}					
					NodeIterator objMapColStmts = model.listObjectsOfProperty(objNode, columnProp);
					while (objMapColStmts.hasNext()) {
						RDFNode colNode = objMapColStmts.next(); 
						objMap = new ObjectMap(getNewObjectMapId(objectMapCounter++), 
								TemplateTermSetBuilder.constructTemplateTermSetFromR2rmlColumnString(
										colNode.toString(), formatter), rdfLiteralTypeTermSet, objMapLangTermSet);
					}
					if(objMap == null)
					{
						NodeIterator templateItr = model.listObjectsOfProperty(objNode, templateProp);
						//try a literal/constant node
						if(templateItr == null ||  !templateItr.hasNext()){
							templateItr = model.listObjectsOfProperty(objNode, constantProp);
						}
						TemplateTermSet objTemplTermSet = null;
						while (templateItr.hasNext()) {
							RDFNode templNode = templateItr.next();
							String template = templNode.toString();
							boolean isUri = !templNode.isLiteral();
							objTemplTermSet = TemplateTermSetBuilder.constructTemplateTermSetFromR2rmlTemplateString(
								template, isUri, kr2rmlMapping.getColumnNameFormatter());
						
						}
						objMap = new ObjectMap(getNewObjectMapId(objectMapCounter++), 
								objTemplTermSet, rdfLiteralTypeTermSet, objMapLangTermSet);
					}
					// Check if anything needs to be added to the columnNameToPredicateObjectMap Map
					if(objMap != null)
						addColumnNameToPredObjectMapLink(objMap, pom, kr2rmlMapping);
				}
			}
			pom.setObject(objMap);
			trMap.addPredicateObjectMap(pom);
		}
		
	
		// Try to add template to pom
			TemplateTermSet subjTemplTermSet = trMap.getSubject().getTemplate();
			if(subjTemplTermSet != null)
			{
				List<TemplateTerm> terms = subjTemplTermSet.getAllTerms();
				if(isValidTemplate(terms))
				{
					PredicateObjectMap pom = new PredicateObjectMap(PredicateObjectMap.getNewId(),trMap);
					Predicate pred = new Predicate(Uris.CLASS_INSTANCE_LINK_URI + "-" + predicateIdCounter++);
					pred.getTemplate().addTemplateTermToSet(
							new StringTemplateTerm(Uris.CLASS_INSTANCE_LINK_URI, true));
					pom.setPredicate(pred);
					StringTemplateTerm rdfLiteralTypeTerm = new StringTemplateTerm("", true);
					TemplateTermSet rdfLiteralTypeTermSet = new TemplateTermSet();
					rdfLiteralTypeTermSet.addTemplateTermToSet(rdfLiteralTypeTerm);
					ObjectMap objMap = new ObjectMap(getNewObjectMapId(objectMapCounter++), 
							subjTemplTermSet, rdfLiteralTypeTermSet);
					pom.setObject(objMap);
					trMap.addPredicateObjectMap(pom);
					addColumnNameToPredObjectMapLink(objMap, pom, kr2rmlMapping);
					
				}
			}
	}

	private boolean isValidTemplate(List<TemplateTerm> terms) {
		if(terms == null || terms.isEmpty())
		{
			return false;
		}
		
		for(TemplateTerm term : terms)
		{
			if(term instanceof ColumnTemplateTerm)
			{
				return true;
			}
		}
		return false;
	}
	
	private void addColumnNameToPredObjectMapLink(ObjectMap objMap, PredicateObjectMap pom, KR2RMLMapping kr2rmlMapping) {
		TemplateTermSet objTermSet = objMap.getTemplate();
		if(objTermSet == null)
		{
			logger.error("No matching object term set");
			return;
		}
		for (TemplateTerm term:objTermSet.getAllTerms()) {
			if (term instanceof ColumnTemplateTerm) {
				String columnName = term.getTemplateTermValue();
				String columnNameWithoutFormatting = kr2rmlMapping.getColumnNameFormatter().getColumnNameWithoutFormatting(columnName);
				List<PredicateObjectMap> existingPomList = kr2rmlMapping.getAuxInfo().
						getColumnNameToPredObjLinks().get(columnNameWithoutFormatting);  
				if (existingPomList == null) {
					existingPomList = new ArrayList<>();
				}
				existingPomList.add(pom);
				kr2rmlMapping.getAuxInfo().getColumnNameToPredObjLinks().put(columnNameWithoutFormatting, existingPomList);
			}
		}
	}


	private String getNewObjectMapId(int objectMapCounter) {
		return "ObjectMap" + objectMapCounter;
	}

	private SubjectMap addSubjectMapForTripleMap(Resource trMapRes, KR2RMLMapping kr2rmlMapping, List<Resource> subjectMapResources) throws  JSONException {
		SubjectMap subjMap = null;
		Property subjMapProp = model.getProperty(Uris.RR_SUBJECTMAP_URI);
		Property templateProp = model.getProperty(Uris.RR_TEMPLATE_URI);
		Property rdfTypeProp = model.getProperty(Uris.RDF_TYPE_URI);
		Property rrClassProp = model.getProperty(Uris.RR_CLASS_URI);
		Resource steinerTreeRootNodeRes = model.getResource(Uris.KM_STEINER_TREE_ROOT_NODE);
		
		NodeIterator subjMapsItr = model.listObjectsOfProperty(trMapRes, subjMapProp);
		while (subjMapsItr.hasNext()){
			Resource subjMapBlankRes = subjMapsItr.next().asResource();
			subjectMapResources.add(subjMapBlankRes);
			
			String subjMapId = subjMapBlankRes.getId().getLabelString();
			subjMap = new SubjectMap(subjMapId);
			kr2rmlMapping.getSubjectMapIndex().put(subjMapId, subjMap);
			
			// Get the subject template
			NodeIterator templateItr = model.listObjectsOfProperty(subjMapBlankRes, templateProp);
			TemplateTermSet subjTemplTermSet = null;
			while (templateItr.hasNext()) {
				RDFNode templNode = templateItr.next();
				String template = templNode.toString();
				subjTemplTermSet = TemplateTermSetBuilder.constructTemplateTermSetFromR2rmlTemplateString(
						template, kr2rmlMapping.getColumnNameFormatter());
				List<String> columnsCovered = new LinkedList<>();
				for(TemplateTerm term : subjTemplTermSet.getAllColumnNameTermElements())
				{
					columnsCovered.add(term.getTemplateTermValue());
				}
				kr2rmlMapping.getAuxInfo().getSubjectMapIdToTemplateAnchor().put(subjMap.getId(), KR2RMLMappingAuxillaryInformation.findSubjectMapTemplateAnchor(columnsCovered));
				
			}
			subjMap.setTemplate(subjTemplTermSet);
			
			// Get the subject type
			NodeIterator rdfTypesItr = model.listObjectsOfProperty(subjMapBlankRes, rrClassProp);
			while (rdfTypesItr.hasNext()) {
				RDFNode typeNode = rdfTypesItr.next();
				
				if (typeNode.isAnon()) {
					NodeIterator typeTemplItr = model.listObjectsOfProperty(typeNode.asResource(),
							templateProp);
					
					while (typeTemplItr.hasNext()) {
						RDFNode templNode = typeTemplItr.next();
						String template = templNode.toString();
						TemplateTermSet typeTermSet = TemplateTermSetBuilder.
								constructTemplateTermSetFromR2rmlTemplateString(
								template);
						subjMap.addRdfsType(typeTermSet);
					}
					continue;
				}
				
				if (typeNode instanceof Resource) {
					// Skip the steiner tree root type
					if(((Resource) typeNode).getURI().equals(Uris.KM_STEINER_TREE_ROOT_NODE))
						continue;
					
					StringTemplateTerm uriTerm = new StringTemplateTerm(
							((Resource) typeNode).getURI(), true);
					TemplateTermSet typeTermSet = new TemplateTermSet();
					typeTermSet.addTemplateTermToSet(uriTerm);
					subjMap.addRdfsType(typeTermSet);
				} else {
					TemplateTermSet typeTermSet = TemplateTermSetBuilder.
							constructTemplateTermSetFromR2rmlTemplateString(
							typeNode.toString());
					subjMap.addRdfsType(typeTermSet);
				}
			}
			
			// Check if it is as the Steiner tree root node
			if (model.contains(subjMapBlankRes, rdfTypeProp, steinerTreeRootNodeRes)) {
				subjMap.setAsSteinerTreeRootNode(true);
			}
		}
		return subjMap;
	}

	private void calculateColumnNodesCoveredByBlankNodes(KR2RMLMapping kr2rmlMapping, List<Resource> subjectMapResources) throws JSONException, KarmaException {
		Property termTypeProp = model.getProperty(Uris.RR_TERM_TYPE_URI);
		Resource blankNodeRes = model.getResource(Uris.RR_BLANK_NODE_URI);
		Property kmBnodePrefixProp = model.getProperty(Uris.KM_BLANK_NODE_PREFIX_URI);
		ResIterator blankNodeSubjMapItr = model.listResourcesWithProperty(termTypeProp, blankNodeRes);
		
		for (Resource subjMapRes:subjectMapResources) {
			
			if (model.contains(subjMapRes, termTypeProp, blankNodeRes)) {
				List<String> columnsCovered = new ArrayList<>();
				Resource blankNodeSubjRes = blankNodeSubjMapItr.next();
				
				SubjectMap subjMap = kr2rmlMapping.getSubjectMapIndex().get(blankNodeSubjRes.getId().getLabelString());
				subjMap.setAsBlankNode(true);
				NodeIterator bnodePrefixItr = model.listObjectsOfProperty(blankNodeSubjRes, kmBnodePrefixProp);
				while (bnodePrefixItr.hasNext()) {
					kr2rmlMapping.getAuxInfo().getBlankNodesUriPrefixMap().put(subjMap.getId(), 
							bnodePrefixItr.next().toString());
				}
				
				TriplesMap mytm;
				for(TriplesMap tm : kr2rmlMapping.getTriplesMapList())
				{
					if(tm.getSubject().getId().equalsIgnoreCase(subjMap.getId()))
					{
						mytm = tm;
						
						List<PredicateObjectMap> poms = mytm.getPredicateObjectMaps();
						for(PredicateObjectMap pom : poms )
						{
							ObjectMap objMap = pom.getObject();
							if(objMap == null)
							{
								logger.error("Unable to find object map for pom :" + pom.toString());
							}
								
							TemplateTermSet templateTermSet = pom.getObject().getTemplate();
							if(templateTermSet != null)
							{
								TemplateTerm term = templateTermSet.getAllTerms().get(0);
								if(term!= null && term instanceof ColumnTemplateTerm)
								{
									columnsCovered.add(term.getTemplateTermValue());
								}
							}
						}
						
						if(columnsCovered.isEmpty())
						{
							//String blankNodeUriPrefix = kr2rmlMapping.getAuxInfo().getBlankNodesUriPrefixMap().get(subjMap.getId());
							//throw new KarmaException("You need to define a URI for "+blankNodeUriPrefix+ ".");
						}
						break;
					}
				}
		
				logger.debug("Adding columns for blank node" + subjMap.getId() + " List: " + 
						columnsCovered);
				kr2rmlMapping.getAuxInfo().getBlankNodesColumnCoverage().put(subjMap.getId(), columnsCovered);
				kr2rmlMapping.getAuxInfo().getSubjectMapIdToTemplateAnchor().put(subjMap.getId(), KR2RMLMappingAuxillaryInformation.findSubjectMapTemplateAnchor(columnsCovered));
				// Get the blank node prefix
				
			}
		}
	}


}
