<commit_before>/**
 *  Copyright 2012 <NAME> <<EMAIL>>
 *
 *  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.
 *
 */
package org.graphity.processor.model.impl;

import com.hp.hpl.jena.datatypes.RDFDatatype;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.query.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.sparql.util.Loader;
import com.hp.hpl.jena.sparql.vocabulary.FOAF;
import com.hp.hpl.jena.update.UpdateFactory;
import com.hp.hpl.jena.update.UpdateRequest;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.DCTerms;
import com.hp.hpl.jena.vocabulary.RDF;
import com.sun.jersey.api.core.ResourceContext;
import java.net.URI;
import java.util.GregorianCalendar;
import java.util.Iterator;
import javax.annotation.PostConstruct;
import javax.naming.ConfigurationException;
import javax.servlet.ServletConfig;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.graphity.processor.exception.NotFoundException;
import org.graphity.processor.model.ContainerResource;
import org.graphity.processor.query.QueryBuilder;
import org.graphity.processor.query.SelectBuilder;
import org.graphity.processor.update.InsertDataBuilder;
import org.graphity.processor.update.UpdateBuilder;
import org.graphity.processor.vocabulary.GP;
import org.graphity.processor.vocabulary.LDP;
import org.graphity.processor.vocabulary.SIOC;
import org.graphity.processor.vocabulary.XHV;
import org.graphity.server.model.GraphStore;
import org.graphity.server.model.impl.QueriedResourceBase;
import org.graphity.server.model.SPARQLEndpoint;
import org.graphity.util.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.topbraid.spin.model.SPINFactory;
import org.topbraid.spin.model.TemplateCall;
import org.topbraid.spin.model.update.Update;
import org.topbraid.spin.vocabulary.SPIN;

/**
 * Base class of generic read-write Graphity Processor resources.
 * Configured declaratively using sitemap ontology to provide Linked Data layer over a SPARQL endpoint.
 * Supports pagination on containers (implemented using SPARQL query solution modifiers).
 * 
 * @author <NAME> <<EMAIL>>
 * @see ContainerResource
 * @see <a href="http://jena.apache.org/documentation/javadoc/jena/com/hp/hpl/jena/ontology/OntResource.html">OntResource</a>
 * @see <a href="http://www.w3.org/TR/sparql11-query/#solutionModifiers">15 Solution Sequences and Modifiers</a>
 */
@Path("/")
public class ResourceBase extends QueriedResourceBase implements org.graphity.processor.model.Resource, ContainerResource
{
    private static final Logger log = LoggerFactory.getLogger(ResourceBase.class);

    private final GraphStore graphStore;
    private final OntClass matchedOntClass;
    private final OntResource ontResource;
    private final ResourceContext resourceContext;
    private final HttpHeaders httpHeaders;
    private String orderBy;
    private Boolean desc;
    private Long limit, offset;
    private QueryBuilder queryBuilder;
    private CacheControl cacheControl;
    
    /**
     * Public JAX-RS constructor. Suitable for subclassing.
     * If the request URI does not match any URI template in the sitemap ontology, 404 Not Found is returned.
     * 
     * If the matching ontology class is a subclass of LDP Page, this resource becomes a page resource and
     * HATEOS metadata is added (relations to the container and previous/next page resources).
     * 
     * @param uriInfo URI information of the current request
     * @param request current request
     * @param servletConfig webapp context
     * @param endpoint SPARQL endpoint of this resource
     * @param graphStore Graph Store of this resource
     * @param ontClass matched ontology class
     * @param httpHeaders HTTP headers of the current request
     * @param resourceContext resource context
     */
    public ResourceBase(@Context UriInfo uriInfo, @Context Request request, @Context ServletConfig servletConfig,
            @Context SPARQLEndpoint endpoint, @Context GraphStore graphStore,
            @Context OntClass ontClass, @Context HttpHeaders httpHeaders, @Context ResourceContext resourceContext)
    {
	super(uriInfo, request, servletConfig, endpoint);

        if (ontClass == null)
        {
            if (log.isDebugEnabled()) log.debug("Resource {} has not matched any template OntClass, returning 404 Not Found", getURI());
            throw new NotFoundException("Resource has not matched any template");
        }
        
	if (graphStore == null) throw new IllegalArgumentException("GraphStore cannot be null");
        if (httpHeaders == null) throw new IllegalArgumentException("HttpHeaders cannot be null");
	if (resourceContext == null) throw new IllegalArgumentException("ResourceContext cannot be null");

        OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
        model.add(ontClass.getModel()); // we don't want to make permanent changes to base ontology which is cached
        this.ontResource = model.createOntResource(getURI());
        this.matchedOntClass = ontClass;
	this.graphStore = graphStore;
	this.httpHeaders = httpHeaders;
        this.resourceContext = resourceContext;

        if (log.isDebugEnabled()) log.debug("Constructing ResourceBase with matched OntClass: {}", matchedOntClass);
    }

    /**
     * Post-construct initialization. Subclasses need to call super.init() first, just like with super() constructor.
     */
    @PostConstruct
    public void init()
    {
        Query query = getQuery(getMatchedOntClass(), SPIN.query, getQuerySolutionMap(this));
        if (query == null)
        {
            if (log.isErrorEnabled()) log.error("Query not defined for template '{}' (spin:query missing)", getMatchedOntClass().getURI());
            throw new WebApplicationException(new ConfigurationException("Query not defined for template '" + getMatchedOntClass().getURI() +"'"));
        }

        if (getMatchedOntClass().hasSuperClass(LDP.Container))
        {
            if (!getUriInfo().getQueryParameters().containsKey(GP.offset.getLocalName()))
            {
                Long defaultOffset = getLongValue(getMatchedOntClass(), GP.offset);
                if (defaultOffset == null) defaultOffset = Long.valueOf(0); // OFFSET is 0 by default
                this.offset = defaultOffset;
            }
            else this.offset = Long.parseLong(getUriInfo().getQueryParameters().getFirst(GP.offset.getLocalName()));
                
            if (!getUriInfo().getQueryParameters().containsKey(GP.limit.getLocalName()))
            {
                Long defaultLimit = getLongValue(getMatchedOntClass(), GP.limit);
                if (defaultLimit == null) throw new IllegalArgumentException("Template class '" + getMatchedOntClass().getURI() + "' must have gp:limit if it is used as container");
                this.limit = defaultLimit;
            }
            else this.limit = Long.parseLong(getUriInfo().getQueryParameters().getFirst(GP.limit.getLocalName()));
                
            if (!getUriInfo().getQueryParameters().containsKey(GP.orderBy.getLocalName())) this.orderBy = getStringValue(getMatchedOntClass(), GP.orderBy);
            else this.orderBy = getUriInfo().getQueryParameters().getFirst(GP.orderBy.getLocalName());
            
            if (!getUriInfo().getQueryParameters().containsKey(GP.desc.getLocalName()))
            {
                Boolean defaultDesc = getBooleanValue(getMatchedOntClass(), GP.desc);
                if (defaultDesc == null) defaultDesc = false; // ORDERY BY is ASC() by default
                this.desc = defaultDesc;
            }
            else this.desc = Boolean.parseBoolean(getUriInfo().getQueryParameters().getFirst(GP.orderBy.getLocalName()));
            
            queryBuilder = setSelectModifiers(QueryBuilder.fromQuery(query, getModel()),
                    this.offset, this.limit, this.orderBy, this.desc);
        }
        else
        {
            this.offset = this.limit = null;
            this.orderBy = null;
            this.desc = null;
            
            queryBuilder = QueryBuilder.fromQuery(query, getModel());
        }
        if (log.isDebugEnabled()) log.debug("Constructing ResourceBase with QueryBuilder: {}", queryBuilder);
        
        cacheControl = getCacheControl(getMatchedOntClass());
    }
    
    public Long getLongValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())
            return ontClass.getPropertyValue(property).asLiteral().getLong();
        
        return null;
    }

    public Boolean getBooleanValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())
            return ontClass.getPropertyValue(property).asLiteral().getBoolean();
        
        return null;
    }

    public String getStringValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())<commit_msg>$gp:ontModel fix (introduced in e660416)<commit_after>/**
 *  Copyright 2012 <NAME> <<EMAIL>>
 *
 *  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.
 *
 */
package org.graphity.processor.model.impl;

import com.hp.hpl.jena.datatypes.RDFDatatype;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.query.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.sparql.util.Loader;
import com.hp.hpl.jena.sparql.vocabulary.FOAF;
import com.hp.hpl.jena.update.UpdateFactory;
import com.hp.hpl.jena.update.UpdateRequest;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.DCTerms;
import com.hp.hpl.jena.vocabulary.RDF;
import com.sun.jersey.api.core.ResourceContext;
import java.net.URI;
import java.util.GregorianCalendar;
import java.util.Iterator;
import javax.annotation.PostConstruct;
import javax.naming.ConfigurationException;
import javax.servlet.ServletConfig;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.graphity.processor.exception.NotFoundException;
import org.graphity.processor.model.ContainerResource;
import org.graphity.processor.query.QueryBuilder;
import org.graphity.processor.query.SelectBuilder;
import org.graphity.processor.update.InsertDataBuilder;
import org.graphity.processor.update.UpdateBuilder;
import org.graphity.processor.vocabulary.GP;
import org.graphity.processor.vocabulary.LDP;
import org.graphity.processor.vocabulary.SIOC;
import org.graphity.processor.vocabulary.XHV;
import org.graphity.server.model.GraphStore;
import org.graphity.server.model.impl.QueriedResourceBase;
import org.graphity.server.model.SPARQLEndpoint;
import org.graphity.util.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.topbraid.spin.model.SPINFactory;
import org.topbraid.spin.model.TemplateCall;
import org.topbraid.spin.model.update.Update;
import org.topbraid.spin.vocabulary.SPIN;

/**
 * Base class of generic read-write Graphity Processor resources.
 * Configured declaratively using sitemap ontology to provide Linked Data layer over a SPARQL endpoint.
 * Supports pagination on containers (implemented using SPARQL query solution modifiers).
 * 
 * @author <NAME> <<EMAIL>>
 * @see ContainerResource
 * @see <a href="http://jena.apache.org/documentation/javadoc/jena/com/hp/hpl/jena/ontology/OntResource.html">OntResource</a>
 * @see <a href="http://www.w3.org/TR/sparql11-query/#solutionModifiers">15 Solution Sequences and Modifiers</a>
 */
@Path("/")
public class ResourceBase extends QueriedResourceBase implements org.graphity.processor.model.Resource, ContainerResource
{
    private static final Logger log = LoggerFactory.getLogger(ResourceBase.class);

    private final GraphStore graphStore;
    private final OntClass matchedOntClass;
    private final OntResource ontResource;
    private final ResourceContext resourceContext;
    private final HttpHeaders httpHeaders;
    private String orderBy;
    private Boolean desc;
    private Long limit, offset;
    private QueryBuilder queryBuilder;
    private CacheControl cacheControl;
    
    /**
     * Public JAX-RS constructor. Suitable for subclassing.
     * If the request URI does not match any URI template in the sitemap ontology, 404 Not Found is returned.
     * 
     * If the matching ontology class is a subclass of LDP Page, this resource becomes a page resource and
     * HATEOS metadata is added (relations to the container and previous/next page resources).
     * 
     * @param uriInfo URI information of the current request
     * @param request current request
     * @param servletConfig webapp context
     * @param endpoint SPARQL endpoint of this resource
     * @param graphStore Graph Store of this resource
     * @param ontClass matched ontology class
     * @param httpHeaders HTTP headers of the current request
     * @param resourceContext resource context
     */
    public ResourceBase(@Context UriInfo uriInfo, @Context Request request, @Context ServletConfig servletConfig,
            @Context SPARQLEndpoint endpoint, @Context GraphStore graphStore,
            @Context OntClass ontClass, @Context HttpHeaders httpHeaders, @Context ResourceContext resourceContext)
    {
	super(uriInfo, request, servletConfig, endpoint);

        if (ontClass == null)
        {
            if (log.isDebugEnabled()) log.debug("Resource {} has not matched any template OntClass, returning 404 Not Found", getURI());
            throw new NotFoundException("Resource has not matched any template");
        }
        
	if (graphStore == null) throw new IllegalArgumentException("GraphStore cannot be null");
        if (httpHeaders == null) throw new IllegalArgumentException("HttpHeaders cannot be null");
	if (resourceContext == null) throw new IllegalArgumentException("ResourceContext cannot be null");

        OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
        model.add(ontClass.getModel()); // we don't want to make permanent changes to base ontology which is cached
        this.ontResource = model.createOntResource(getURI());
        this.matchedOntClass = ontClass;
	this.graphStore = graphStore;
	this.httpHeaders = httpHeaders;
        this.resourceContext = resourceContext;

        if (log.isDebugEnabled()) log.debug("Constructing ResourceBase with matched OntClass: {}", matchedOntClass);
    }

    /**
     * Post-construct initialization. Subclasses need to call super.init() first, just like with super() constructor.
     */
    @PostConstruct
    public void init()
    {
	if (log.isDebugEnabled()) log.debug("OntResource {} gets type of OntClass: {}", this, getMatchedOntClass());
	addProperty(RDF.type, getMatchedOntClass()); // getOntModel().add(description); ?
        
        Query query = getQuery(getMatchedOntClass(), SPIN.query, getQuerySolutionMap(this));
        if (query == null)
        {
            if (log.isErrorEnabled()) log.error("Query not defined for template '{}' (spin:query missing)", getMatchedOntClass().getURI());
            throw new WebApplicationException(new ConfigurationException("Query not defined for template '" + getMatchedOntClass().getURI() +"'"));
        }

        if (getMatchedOntClass().hasSuperClass(LDP.Container))
        {
            if (!getUriInfo().getQueryParameters().containsKey(GP.offset.getLocalName()))
            {
                Long defaultOffset = getLongValue(getMatchedOntClass(), GP.offset);
                if (defaultOffset == null) defaultOffset = Long.valueOf(0); // OFFSET is 0 by default
                this.offset = defaultOffset;
            }
            else this.offset = Long.parseLong(getUriInfo().getQueryParameters().getFirst(GP.offset.getLocalName()));
                
            if (!getUriInfo().getQueryParameters().containsKey(GP.limit.getLocalName()))
            {
                Long defaultLimit = getLongValue(getMatchedOntClass(), GP.limit);
                if (defaultLimit == null) throw new IllegalArgumentException("Template class '" + getMatchedOntClass().getURI() + "' must have gp:limit if it is used as container");
                this.limit = defaultLimit;
            }
            else this.limit = Long.parseLong(getUriInfo().getQueryParameters().getFirst(GP.limit.getLocalName()));
                
            if (!getUriInfo().getQueryParameters().containsKey(GP.orderBy.getLocalName())) this.orderBy = getStringValue(getMatchedOntClass(), GP.orderBy);
            else this.orderBy = getUriInfo().getQueryParameters().getFirst(GP.orderBy.getLocalName());
            
            if (!getUriInfo().getQueryParameters().containsKey(GP.desc.getLocalName()))
            {
                Boolean defaultDesc = getBooleanValue(getMatchedOntClass(), GP.desc);
                if (defaultDesc == null) defaultDesc = false; // ORDERY BY is ASC() by default
                this.desc = defaultDesc;
            }
            else this.desc = Boolean.parseBoolean(getUriInfo().getQueryParameters().getFirst(GP.orderBy.getLocalName()));
            
            queryBuilder = setSelectModifiers(QueryBuilder.fromQuery(query, getModel()),
                    this.offset, this.limit, this.orderBy, this.desc);
        }
        else
        {
            this.offset = this.limit = null;
            this.orderBy = null;
            this.desc = null;
            
            queryBuilder = QueryBuilder.fromQuery(query, getModel());
        }
        
        //if (log.isDebugEnabled()) log.debug("Constructing ResourceBase with QueryBuilder: {}", queryBuilder);
	//getQueryBuilder().build(); // sets sp:text value
	if (log.isDebugEnabled()) log.debug("OntResource {} gets explicit spin:query value {}", this, queryBuilder);
	addProperty(SPIN.query, getQueryBuilder());
        
        cacheControl = getCacheControl(getMatchedOntClass());        
    }
    
    public Long getLongValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())
            return ontClass.getPropertyValue(property).asLiteral().getLong();
        
        return null;
    }

    public Boolean getBooleanValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())
            return ontClass.getPropertyValue(property).asLiteral().getBoolean();
        
        return null;
    }

    public String getStringValue(OntClass ontClass, DatatypeProperty property)
    {
        if (ontClass.hasProperty(property) && ontClass.getPropertyValue(property).isLiteral())
{'fraction_non_alphanumeric': 0.1023696682464455, 'fraction_numerical': 0.0025941631329508604, 'mean_word_length': 3.4408506867523263, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2369859', 'n_tokens_mistral': 5506, 'n_tokens_neox': 5409, 'n_words': 1384}