/*
//
// Licensed to Benedikt Kämpgen under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Benedikt Kämpgen licenses this file to you 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.olap4j.driver.olap4ld.linkeddata;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olap4j.OlapException;
import org.olap4j.Position;
import org.olap4j.driver.olap4ld.Olap4ldUtil;
import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil;
import org.olap4j.metadata.Cube;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Measure;
import org.semanticweb.yars.nx.Literal;
import org.semanticweb.yars.nx.Node;
import org.semanticweb.yars.nx.Variable;
import org.semanticweb.yars.nx.parser.NxParser;
/**
* Implements methods of XmlaOlap4jDatabaseMetadata, returning the specified
* columns as nodes from a sparql endpoint.
*
* @author b-kaempgen
*
*/
public class SesameEngine implements LinkedDataCubesEngine {
/**
* The type of SPARQL endpoint should be found out automatically and with
* the server string
*/
private static String SPARQLSERVERURL;
private static final String STANDARDPREFIX = "prefix owl: <http://www.w3.org/2002/07/owl#> prefix skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sdmx-measure: <http://purl.org/linked-data/sdmx/2009/measure#>\n PREFIX dcterms: <http://purl.org/dc/terms/>\n PREFIX eus: <http://ontologycentral.com/2009/01/eurostat/ns#>\n PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n PREFIX qb: <http://purl.org/linked-data/cube#>\n PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n PREFIX dc: <http://purl.org/dc/elements/1.1/>\n PREFIX skosclass: <http://ddialliance.org/ontologies/skosclass#>\n ";
private static final String DATASOURCEDESCRIPTION = "OLAP data from the statistical Linked Data cloud.";
private static final String PROVIDERNAME = "The community.";
private static String URL;
private static final String DATASOURCEINFO = "Data following the Linked Data principles.";
private static final String CATALOG_NAME = "LdCatalog";
private static final String DESCRIPTION = "Catalog of Linked Data.";
private static final String SCHEMA_NAME = "LdSchema";
/*
* Array of datasets of the database/catalog/schema that shall be integrated
*/
public String DATASOURCENAME;
public String DATASOURCEVERSION;
private ArrayList<String> datastructuredefinitions;
private ArrayList<String> datasets;
private HashMap<Integer, List<Node[]>> sparqlResultMap = new HashMap<Integer, List<Node[]>>();
public SesameEngine(URL serverUrlObject,
ArrayList<String> datastructuredefinitions,
ArrayList<String> datasets, String databasename) {
URL = serverUrlObject.toString();
this.datastructuredefinitions = datastructuredefinitions;
this.datasets = datasets;
// TODO: I could add another version with a specific rule set for open
// virtuoso
if (databasename.equals("SESAME")) {
this.DATASOURCENAME = databasename;
this.DATASOURCEVERSION = "1.0";
}
// Set URL
SPARQLSERVERURL = serverUrlObject.toString();
}
/**
* Returns from String in order to retrieve information about these URIs
*
*
* Properly access the triple store: For DSD, we query a specific named
* graph that we put the DSDs in (could be much better). For data, we query
* the default graph.
*
* @param uris
* @return fromResult
*/
private String askForFrom(boolean isDsdQuery) {
/*
* Depending on whether we want to query for dsd or data, we ask
* different named graphs
*/
String fromResult = " ";
if (isDsdQuery) {
// create a from from dsd parameter
for (String dsdGraph : datastructuredefinitions) {
fromResult += "from <" + dsdGraph + "> ";
}
} else {
// We query for all ds parameters
for (String dsGraph : datasets) {
fromResult += "from <" + dsGraph + "> ";
}
}
return fromResult;
}
/**
* Helper Method for asking for location
*
* @param uri
* @return
* @throws MalformedURLException
* @throws IOException
*/
@SuppressWarnings("unused")
private String askForLocation(String uri) throws MalformedURLException {
URL url;
url = new URL(uri);
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection connection;
try {
connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Accept", "application/rdf+xml");
String header = connection.getHeaderField("location");
// TODO: Could be that we need to check whether bogus comes out
// (e.g., Not found).
if (header != null) {
if (header.startsWith("http:")) {
uri = header;
} else {
// Header only contains the local uri
// Cut all off until first / from the back
int index = uri.lastIndexOf("/");
uri = uri.substring(0, index + 1) + header;
}
}
} catch (IOException e) {
throw new MalformedURLException(e.getMessage());
}
if (uri.endsWith(".ttl")) {
throw new MalformedURLException(
"Qcrumb cannot handle non-rdf files, yet");
}
return uri;
}
/**
* I think, caching some sparql results would be very useful.
*
* I create a map between hash value of sparql query and the Nodes.
*
* If the value is available, I return it.
*
* However, when to empty the cache? I empty the cache if I populate a new
* cube.
*
* @param query
* @param caching
* @return
*/
private List<Node[]> sparql(String query, Boolean caching) {
Integer hash = null;
if (caching) {
hash = query.hashCode();
}
if (caching) {
if (this.sparqlResultMap.containsKey(hash)) {
return this.sparqlResultMap.get(hash);
}
}
Olap4ldUtil._log.info("SPARQL query: " + query);
List<Node[]> result;
result = sparql(query);
if (caching) {
if (this.sparqlResultMap != null
&& !this.sparqlResultMap.containsKey(hash)) {
this.sparqlResultMap.put(hash, result);
} else {
// TODO: Problem: If this method is run simultaneously, it could
// be that this happens.
// throw new UnsupportedOperationException(
// "Hash key should not be contained.");
}
}
return result;
}
/**
* If new cube is created, I empty the cache of the Linked Data Engine
*/
public void emptySparqlResultCache() {
this.sparqlResultMap.clear();
}
private List<Node[]> sparql(String query) {
List<Node[]> myBindings = new ArrayList<Node[]>();
try {
// Better? ISO-8859-1
String querysuffix = "?query=" + URLEncoder.encode(query, "UTF-8");
/*
* Curly brackets should not be encoded, says qcrumb
*
* querysuffix = querysuffix.replaceAll("%7B", "{"); querysuffix =
* querysuffix.replaceAll("%7D", "}");
*
* Stimmt nicht: Das Problem ist NX-Darstellung und leere bindings.
*
* Momentan wird dazu einfach <null> verwendet.
*/
// String acceptsuffix = "&accept=text%2Fn3";
String fullurl = SPARQLSERVERURL + querysuffix;
// String fullurl =
// "http://qcrumb.com/sparql?query=PREFIX+sdmx-measure%3A+<http%3A%2F%2Fpurl.org%2Flinked-data%2Fsdmx%2F2009%2Fmeasure%23>%0D%0APREFIX+dcterms%3A+<http%3A%2F%2Fpurl.org%2Fdc%2Fterms%2F>%0D%0APREFIX+eus%3A+<http%3A%2F%2Fontologycentral.com%2F2009%2F01%2Feurostat%2Fns%23>%0D%0APREFIX+rdf%3A+<http%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23>%0D%0APREFIX+qb%3A+<http%3A%2F%2Fpurl.org%2Flinked-data%2Fcube%23>%0D%0APREFIX+rdfs%3A+<http%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23>%0D%0A%0D%0ASELECT+%3Ftime+%3Fvalue+%3Fgeo%0D%0AFROM+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fdata%2Ftsieb020>%0D%0AFROM+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fdic%2Fgeo>%0D%0AWHERE+{%0D%0A++%3Fs+qb%3Adataset+<http%3A%2F%2Festatwrap.ontologycentral.com%2Fid%2Ftsieb020%23ds>+.%0D%0A++%3Fs+dcterms%3Adate+%3Ftime+.%0D%0A++%3Fs+eus%3Ageo+%3Fg+.%0D%0A++%3Fg+rdfs%3Alabel+%3Fgeo+.%0D%0A++%3Fs+sdmx-measure%3AobsValue+%3Fvalue+.%0D%0A++FILTER+(lang(%3Fgeo)+%3D+\"\")%0D%0A}+ORDER+BY+%3Fgeo%0D%0A&rules=&accept=application%2Fsparql-results%2Bjson"
HttpURLConnection con = (HttpURLConnection) new URL(fullurl)
.openConnection();
// TODO: How to properly set header? Does not work therefore
// manually
// added
// con.setRequestProperty("Accept",
// "application/sparql-results+json");
// con.setRequestProperty("Accept", "text/n3");
con.setRequestProperty("Accept", "application/sparql-results+xml, */*;q=0.5");
con.setRequestMethod("POST");
InputStream inputStream = con.getInputStream();
String test = Olap4ldLinkedDataUtil.convertStreamToString(inputStream);
Olap4ldUtil._log.info("XML output: " + test);
if (con.getResponseCode() != 200) {
throw new RuntimeException("lookup on " + fullurl
+ " resulted HTTP in status code "
+ con.getResponseCode());
}
// Transform sparql xml to nx
InputStream nx = Olap4ldLinkedDataUtil.transformSparqlXmlToNx(con
.getInputStream());
String test2 = Olap4ldLinkedDataUtil.convertStreamToString(nx);
Olap4ldUtil._log.info("NX output: " + test2);
nx.reset();
NxParser nxp = new NxParser(nx);
Node[] nxx;
while (nxp.hasNext()) {
try {
nxx = nxp.next();
myBindings.add(nxx);
} catch (Exception e) {
Olap4ldUtil._log
.warning("NxParser: Could not parse properly: "
+ e.getMessage());
}
;
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return myBindings;
}
private List<Node[]> applyRestrictions(List<Node[]> result,
Restrictions restrictions) {
/*
* We go through all the nodes. For each node, if it does not fulfill
* one of the restrictions, we remove that node from the result (lets
* see how that works).
*
* Removing is bad, since this result may come from a cache. Therefore I
* create an add list and then return this.
*/
boolean isFirst = true;
Map<String, Integer> mapFields = null;
ArrayList<Node[]> addList = new ArrayList<Node[]>();
// for (Node[] node : result) {
// boolean isToRemove = false;
// if (isFirst) {
// mapFields = Olap4ldLinkedDataUtil.getNodeResultFields(node);
// isFirst = false;
// } else {
// // restrictions are structured: %2=0 restriction name, %2=1
// // restriction value
// for (int i = 0; i <= restrictions.length - 1; i = i + 2) {
// // TREE_OP is special, needs to be handled differently
// if (restrictions[i].toString().equals("TREE_OP")) {
//
// // TODO: In this case, we should prioritize treeOp
//
// int tree = new Integer(restrictions[i + 1].toString());
// if ((tree & 1) == 1) {
// // CHILDREN
// Olap4ldUtil._log.info("TreeOp:CHILDREN");
// } else {
// // Remove all children
// // TODO: So far, we do not support children.
// }
// if ((tree & 2) == 2) {
// // SIBLINGS
// Olap4ldUtil._log.info("TreeOp:SIBLINGS");
//
// } else {
// // How to remove all siblings?
// /*
// * We can assume that we are at a specific cube,
// * dim, hier, level
// */
// }
// if ((tree & 4) == 4) {
// // PARENT
// Olap4ldUtil._log.info("TreeOp:PARENT");
// } else {
// // Remove all parents
// // TODO: So far, we do not support parents.
// }
// if ((tree & 8) == 8) {
// // SELF
// Olap4ldUtil._log.info("TreeOp:SELF");
// } else {
// // How can we remove only oneself?
//
// }
// if ((tree & 16) == 16) {
// // DESCENDANTS
// Olap4ldUtil._log.info("TreeOp:DESCENDANTS");
//
// } else {
// // Remove all descendants
// // TODO: So far, we do not support descendants.
// }
// if ((tree & 32) == 32) {
// // ANCESTORS
// Olap4ldUtil._log.info("TreeOp:ANCESTORS");
// } else {
// // Remove all ancestors
// // TODO: So far, we do not support ancestors.
// }
//
// } else {
//
// String restriction = restrictions[i + 1].toString();
// String value = Olap4ldLinkedDataUtil
// .convertNodeToMDX(node[mapFields.get("?"
// + restrictions[i].toString())]);
//
// if (restriction == null || restriction.equals(value)) {
// // fine
// } else {
// isToRemove = true;
// // TODO does it jump out of the whole thing?
// // We need to go to the next node
// break;
// }
// }
// }
// }
// // If node is marked as delete, nothing, else add it to addList
// if (!isToRemove) {
// addList.add(node);
// }
// }
return addList;
}
/**
*
* @return
*/
public List<Node[]> getDatabases() {
/*
* DISCOVER_DATASOURCES(new MetadataColumn("DataSourceName"), new
* MetadataColumn("DataSourceDescription"), new MetadataColumn("URL"),
* new MetadataColumn("DataSourceInfo"), new MetadataColumn(
* "ProviderName"), new MetadataColumn("ProviderType"), new
* MetadataColumn("AuthenticationMode")),
*/
List<Node[]> results = new ArrayList<Node[]>();
Node[] bindingNames = new Node[] { new Variable("?DATA_SOURCE_NAME"),
new Variable("?DATA_SOURCE_DESCRIPTION"),
new Variable("?PROVIDER_NAME"), new Variable("?URL"),
new Variable("?DATA_SOURCE_INFO") };
results.add(bindingNames);
Node[] triple = new Node[] { new Literal(DATASOURCENAME),
new Literal(DATASOURCEDESCRIPTION), new Literal(PROVIDERNAME),
new Literal(URL), new Literal(DATASOURCEINFO) };
results.add(triple);
return results;
}
public List<Node[]> getCatalogs() {
/*
* DBSCHEMA_CATALOGS( new MetadataColumn("CATALOG_NAME"), new
* MetadataColumn( "DESCRIPTION"), new MetadataColumn("ROLES"), new
* MetadataColumn("DATE_MODIFIED"))
*/
List<Node[]> results = new ArrayList<Node[]>();
Node[] bindingNames = new Node[] { new Variable("?CATALOG_NAME"),
new Variable("?DESCRIPTION"), new Variable("?ROLES"),
new Variable("?DATE_MODIFIED") };
results.add(bindingNames);
Node[] triple = new Node[] { new Literal(CATALOG_NAME),
new Literal(DESCRIPTION),
// No role and date modified
new Literal(""), new Literal(""), };
results.add(triple);
return results;
}
/**
*
* @return
*/
public List<Node[]> getSchemas() {
List<Node[]> results = new ArrayList<Node[]>();
/*
* DBSCHEMA_SCHEMATA(new MetadataColumn( "CATALOG_NAME"), new
* MetadataColumn("SCHEMA_NAME"), new MetadataColumn("SCHEMA_OWNER"))
*/
Node[] bindingNames = new Node[] { new Variable("?CATALOG_NAME"),
new Variable("?SCHEMA_NAME"), new Variable("?SCHEMA_OWNER") };
results.add(bindingNames);
Node[] triple = new Node[] { new Literal(CATALOG_NAME),
new Literal(SCHEMA_NAME),
// No owner
new Literal("") };
results.add(triple);
return results;
}
/**
*
* Get Cubes from the triple store.
*
* Here, the restrictions are strict restrictions without patterns.
*
* ==Task: Show proper captions== Problem: Where to take captions from?
* rdfs:label Problem: There might be several rdfs:label -> only English
* When creating such dsds, we could give the dsd an english label Also, we
* need an english label for dimension, hierarchy, level, members Cell
* Values will be numeric and not require language
*
* @return Node[]{}
*/
public List<Node[]> getCubes(Restrictions restrictions) throws OlapException {
// If new cube is created, I empty the cache of the Linked Data Engine
this.emptySparqlResultCache();
/*
* To improve performance, we filter for certain cubes. We either find
* the cube name in the context or in the restrictions.
*
* Question: How do we cache? For all metadata queries, we cache the
* SPARQL queries. If new cube is created, I empty the cache of the
* Linked Data Engine.
*/
// String cubeURI = null;
// if (context != null && context.olap4jCube != null) {
// // The name should not have any brackets.
// cubeURI = LdOlap4jUtil
// .decodeUriForMdx(context.olap4jCube.getName());
// }
// for (int i = 0; i < restrictions.length; i++) {
// if (restrictions[i].equals("CUBE_NAME")) {
// cubeURI = LdOlap4jUtil.decodeUriForMdx(restrictions[i + 1]
// .toString());
// break;
// }
// }
// TODO: For now, we only use the non-language-tag CAPTION and
// DESCRIPTION
String query = STANDARDPREFIX
+ "select ?CATALOG_NAME ?SCHEMA_NAME ?CUBE_NAME ?CUBE_TYPE ?DESCRIPTION ?CUBE_CAPTION "
+ askForFrom(true)
+ "where { BIND('"+CATALOG_NAME+"' as ?CATALOG_NAME). BIND('"+SCHEMA_NAME+"' as ?SCHEMA_NAME). BIND('CUBE' as ?CUBE_TYPE). ?CUBE_NAME a qb:DataStructureDefinition. OPTIONAL {?CUBE_NAME rdfs:label ?CUBE_CAPTION FILTER ( lang(?CUBE_CAPTION) = \"\" )} OPTIONAL {?CUBE_NAME rdfs:comment ?DESCRIPTION FILTER ( lang(?DESCRIPTION) = \"\" )} }"
+ "order by ?CUBE_NAME ";
List<Node[]> cubeUris = sparql(query, true);
/*
* Check on restrictions that the interface makes:
*
* Restrictions are strong restrictions, no fuzzy, since those wild
* cards have been eliminated before.
*/
List<Node[]> result = applyRestrictions(cubeUris, restrictions);
return result;
}
/**
* Get possible dimensions (component properties) for each cube from the
* triple store.
*
* Approach: I create the output from Linked Data, and then I filter it
* using the restrictions.
*
* I have to also return the Measures dimension for each cube.
*
* @return Node[]{?dsd ?dimension ?compPropType ?name}
* @throws MalformedURLException
*/
public List<Node[]> getDimensions(Restrictions restrictions) throws OlapException {
// Get all dimensions
String query = "";
query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME as ?DIMENSION_NAME ?DIMENSION_UNIQUE_NAME ?DIMENSION_CAPTION ?DESCRIPTION \"0\" as ?DIMENSION_TYPE \"0\" as ?DIMENSION_ORDINAL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:label ?DIMENSION_CAPTION FILTER ( lang(?DIMENSION_CAPTION) = \"\" )} OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:comment ?DESCRIPTION FILTER ( lang(?DESCRIPTION) = \"\" )} } "
+ "order by ?CUBE_NAME ?DIMENSION_NAME ";
List<Node[]> dimensionUris = sparql(query, true);
/*
* Get all measures: We query for all cubes and simply add measure
* information
*/
List<Node[]> result = applyRestrictions(dimensionUris, restrictions);
query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME \"Measures\" as ?DIMENSION_NAME \"Measures\" as ?DIMENSION_UNIQUE_NAME \"Measures\" as ?DIMENSION_CAPTION \"Measures\" as ?DESCRIPTION \"2\" as ?DIMENSION_TYPE \"0\" as ?DIMENSION_ORDINAL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:measure ?DIMENSION_UNIQUE_NAME. OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:label ?DIMENSION_CAPTION FILTER ( lang(?DIMENSION_CAPTION) = \"\" )} } "
+ "order by ?CUBE_NAME ?DIMENSION_NAME limit 1";
List<Node[]> memberUris2 = sparql(query, true);
List<Node[]> result2 = applyRestrictions(memberUris2, restrictions);
// Add all of result2 to result
boolean first = true;
for (Node[] nodes : result2) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
return result;
}
/**
* Every measure also needs to be listed as member. When I create the dsd, I
* add obsValue as a dimension, but also as a measure. However, members of
* the measure dimension would typically all be named differently from the
* measure (e.g., obsValue5), therefore, we do not find a match. The problem
* is, that getMembers() has to return the measures. So, either, in the dsd,
* we need to add a dimension with the measure as a member, or, the query
* for the members should return for measures the measure property as
* member.
*
*
* Here, all the measure properties are returned.
*
* @param context
* @param metadataRequest
* @param restrictions
* @return
*/
public List<Node[]> getMeasures(Restrictions restrictions) throws OlapException {
// ///////////QUERY//////////////////////////
/*
* TODO: How to consider equal measures?
*/
// Boolean values need to be returned as "true" or "false".
// Get all measures
String query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME as ?MEASURE_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?MEASURE_NAME ?DIMENSION_UNIQUE_NAME as ?MEASURE_CAPTION \"5\" as ?DATA_TYPE \"true\" as ?MEASURE_IS_VISIBLE \"5\" as ?MEASURE_AGGREGATOR "
+ askForFrom(true)
// Important: Only measures are queried
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:measure ?DIMENSION_UNIQUE_NAME. OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:label ?MEASURE_CAPTION FILTER ( lang(?MEASURE_CAPTION) = \"\" )} OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:comment ?DESCRIPTION FILTER ( lang(?DESCRIPTION) = \"\" )} } "
+ "order by ?CUBE_NAME ?MEASURE_NAME ";
List<Node[]> measureUris = sparql(query, true);
List<Node[]> result = applyRestrictions(measureUris, restrictions);
return result;
}
/**
*
* Return hierarchies
*
* @param context
* @param metadataRequest
* @param restrictions
* @return
*/
public List<Node[]> getHierarchies(Restrictions restrictions) throws OlapException {
// Get all hierarchies
String query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME as ?HIERARCHY_NAME ?HIERARCHY_CAPTION ?DESCRIPTION "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. OPTIONAL {?HIERARCHY_UNIQUE_NAME rdfs:label ?HIERARCHY_CAPTION FILTER ( lang(?HIERARCHY_CAPTION) = \"\" ) } OPTIONAL {?HIERARCHY_UNIQUE_NAME rdfs:comment ?DESCRIPTION } } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_NAME ";
List<Node[]> hierarchyResults = sparql(query, true);
// TODO: No sorting done, yet
// TODO: How about dimensions without a hierarchy?
List<Node[]> result = applyRestrictions(hierarchyResults, restrictions);
// Get measure dimensions
query = STANDARDPREFIX
+ "select distinct \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME \"Measures\" as ?DIMENSION_UNIQUE_NAME \"Measures\" as ?HIERARCHY_UNIQUE_NAME \"Measures\" as ?HIERARCHY_NAME \"Measures\" as ?HIERARCHY_CAPTION \"Measures\" as ?DESCRIPTION "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:measure ?DIMENSION_UNIQUE_NAME. "
+ "} order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_NAME limit 1";
List<Node[]> memberUris2 = sparql(query, true);
List<Node[]> result2 = applyRestrictions(memberUris2, restrictions);
// Add all of result2 to result
boolean first = true;
for (Node[] nodes : result2) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
// Get dimension hierarchies without codeList
query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?HIERARCHY_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME as ?HIERARCHY_NAME ?HIERARCHY_CAPTION ?DESCRIPTION "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. FILTER NOT EXISTS { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. } OPTIONAL { ?DIMENSION_UNIQUE_NAME rdfs:label ?HIERARCHY_CAPTION } OPTIONAL {?DIMENSION_UNIQUE_NAME rdfs:comment ?DESCRIPTION } } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ";
List<Node[]> memberUris3 = sparql(query, true);
List<Node[]> result3 = applyRestrictions(memberUris3, restrictions);
// Add all of result2 to result
first = true;
for (Node[] nodes : result3) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
return result;
}
/**
*
* @param context
* @param metadataRequest
* @param restrictions
* @return
*/
public List<Node[]> getLevels(Restrictions restrictions) throws OlapException {
// Get all levels
String query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME as ?LEVEL_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME as ?LEVEL_CAPTION \"Root-Level\" as ?LEVEL_NAME \"N/A\" as ?DESCRIPTION \"0\" as ?LEVEL_NUMBER \"0\" as ?LEVEL_CARDINALITY \"0x0000\" as ?LEVEL_TYPE "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. }"
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_NUMBER ";
List<Node[]> levelResults = sparql(query, true);
List<Node[]> result = applyRestrictions(levelResults, restrictions);
// TODO: No sorting done, yet
// TODO: Add properly modeled level
// Add measures levels
query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME \"Measures\" as ?DIMENSION_UNIQUE_NAME \"Measures\" as ?HIERARCHY_UNIQUE_NAME \"Measures\" as ?LEVEL_UNIQUE_NAME \"Measures\" as ?LEVEL_CAPTION \"Measures\" as ?LEVEL_NAME \"Measures\" as ?DESCRIPTION \"0\" as ?LEVEL_NUMBER \"0\" as ?LEVEL_CARDINALITY \"0x0000\" as ?LEVEL_TYPE "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:measure ?DIMENSION_UNIQUE_NAME. } "
+ "order by ?DIMENSION_UNIQUE_NAME limit 1";
// Second, ask for the measures (which are also members)
List<Node[]> memberUris2 = sparql(query, true);
List<Node[]> result2 = applyRestrictions(memberUris2, restrictions);
// Add all of result2 to result
boolean first = true;
for (Node[] nodes : result2) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
// Add levels for dimensions without codelist
query = STANDARDPREFIX
+ "select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?HIERARCHY_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?LEVEL_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?LEVEL_CAPTION ?DIMENSION_UNIQUE_NAME as ?LEVEL_NAME \"N/A\" as ?DESCRIPTION \"0\" as ?LEVEL_NUMBER \"0\" as ?LEVEL_CARDINALITY \"0x0000\" as ?LEVEL_TYPE "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec "
+ ". ?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. FILTER NOT EXISTS { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. } } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?LEVEL_NUMBER ";
// Second, ask for the measures (which are also members)
List<Node[]> memberUris3 = sparql(query, true);
List<Node[]> result3 = applyRestrictions(memberUris3, restrictions);
// Add all of result3 to result
first = true;
for (Node[] nodes : result3) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
return result;
}
/**
* Important issues to remember: Every measure also needs to be listed as
* member. When I create the dsd, I add obsValue as a dimension, but also as
* a measure. However, members of the measure dimension would typically all
* be named differently from the measure (e.g., obsValue5), therefore, we do
* not find a match. The problem is, that getMembers() has to return the
* measures. So, either, in the dsd, we need to add a dimension with the
* measure as a member, or, the query for the members should return for
* measures the measure property as member.
*
* The dimension/hierarchy/level of a measure should always be "Measures".
*
* Typically, a measure should not have a codeList, since we can have many
* many members. If a measure does not have a codelist, the bounding would
* still work, since The componentProperty is existing, but no hierarchy...
*
* For caption of members, we use
* http://www.w3.org/2004/02/skos/core#notation skos:notation, since members
* are in rdf represented as skos:Concept and this is the proper way to give
* them a representation.
*
* @return Node[]{?memberURI ?name}
* @throws MalformedURLException
*/
public List<Node[]> getMembers(Restrictions restrictions) throws OlapException {
/*
* For each dimension, get the possible members
*/
// TODO: Ordering misses ORDINAL (the most important one) - Ad-hoc
// workaround we replace it by Member unique name
// TODO: Currently, no special lang filtered for in caption: FILTER (
// lang(?MEMBER_CAPTION) = \"\")
// First, ask for all members
// TODO: Add members of properly modeled levels
// query = STANDARDPREFIX
// + "Select \""
// + CATALOG_NAME
// + "\" as ?CATALOG_NAME \""
// + SCHEMA_NAME
// +
// "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_UNIQUE_NAME ?LEVEL_NUMBER ?MEMBER_NAME ?MEMBER_UNIQUE_NAME ?MEMBER_CAPTION \"1\" as ?MEMBER_TYPE ?PARENT_UNIQUE_NAME ?PARENT_LEVEL "
// + askForFrom(true)
// + " where { ?CUBE_NAME qb:component ?compSpec. "
// +
// "?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. { { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. ?MEMBER_UNIQUE_NAME skos:member ?LEVEL_UNIQUE_NAME. ?LEVEL_UNIQUE_NAME skos:inScheme ?HIERARCHY_UNIQUE_NAME. ?LEVEL_UNIQUE_NAME skos:depth ?LEVEL_NUMBER. } UNION { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. ?HIERARCHY_UNIQUE_NAME skos:hasTopConcept ?MEMBER_UNIQUE_NAME. }}"
// +
// " OPTIONAL{ ?relationship rdf:type skos:ConceptAssociation. ?relationship skos:inScheme ?HIERARCHY_UNIQUE_NAME. ?relationship skos:hasDomainConcept ?MEMBER_UNIQUE_NAME. ?relationship rdf:predicate skos:broaderTransitive. ?relationship skos:hasRangeConcept ?PARENT_UNIQUE_NAME. ?PARENT_UNIQUE_NAME skos:member ?PARENT_LEVEL_UNIQUE_NAME. ?PARENT_LEVEL_UNIQUE_NAME skos:depth ?PARENT_LEVEL. } "
// +
// " OPTIONAL {?MEMBER_UNIQUE_NAME skos:notation ?MEMBER_CAPTION FILTER ( lang(?MEMBER_CAPTION) = \"\" )} } "
// +
// "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_UNIQUE_NAME ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME";
// TODO: Quickfix distinct (!)
String query = STANDARDPREFIX
+ "Select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME as ?LEVEL_UNIQUE_NAME \"0\" as ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME as ?MEMBER_NAME ?MEMBER_UNIQUE_NAME ?MEMBER_CAPTION \"1\" as ?MEMBER_TYPE \"null\" as ?PARENT_UNIQUE_NAME \"null\" as ?PARENT_LEVEL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec. "
+ "?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. ?HIERARCHY_UNIQUE_NAME skos:hasTopConcept ?MEMBER_UNIQUE_NAME. "
+ " OPTIONAL {?MEMBER_UNIQUE_NAME skos:notation ?MEMBER_CAPTION } } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_UNIQUE_NAME ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME";
List<Node[]> memberUris = sparql(query, true);
List<Node[]> result = applyRestrictions(memberUris, restrictions);
// Second, ask for the measures (which are also members)
query = STANDARDPREFIX
+ "Select \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME \"Measures\" as ?DIMENSION_UNIQUE_NAME \"Measures\" as ?HIERARCHY_UNIQUE_NAME \"Measures\" as ?LEVEL_UNIQUE_NAME \"0\" as ?LEVEL_NUMBER ?DIMENSION_UNIQUE_NAME as ?MEMBER_NAME ?DIMENSION_UNIQUE_NAME as ?MEMBER_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?MEMBER_CAPTION \"3\" as ?MEMBER_TYPE \"\" as ?PARENT_UNIQUE_NAME \"\" as ?PARENT_LEVEL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec. "
+ "?compSpec qb:measure ?DIMENSION_UNIQUE_NAME. } "
+ "order by ?DIMENSION_UNIQUE_NAME";
List<Node[]> memberUris2 = sparql(query, true);
List<Node[]> result2 = applyRestrictions(memberUris2, restrictions);
// Add all of result2 to result
boolean first = true;
for (Node[] nodes : result2) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
/*
* Third, ask for members of all dimensions that do not define a
* codeList. For now, I use separate queries for skos:Concepts and
* Literal values
*/
query = STANDARDPREFIX
+ "Select distinct \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?HIERARCHY_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?LEVEL_UNIQUE_NAME \"0\" as ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME as ?MEMBER_NAME ?MEMBER_UNIQUE_NAME ?MEMBER_CAPTION \"1\" as ?MEMBER_TYPE \"null\" as ?PARENT_UNIQUE_NAME \"null\" as ?PARENT_LEVEL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec. "
+ "?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. FILTER NOT EXISTS { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. } "
+ "?obs qb:dataSet ?ds. ?ds qb:structure ?CUBE_NAME. ?obs ?DIMENSION_UNIQUE_NAME ?MEMBER_UNIQUE_NAME."
+ " ?MEMBER_UNIQUE_NAME skos:notation ?MEMBER_CAPTION } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_UNIQUE_NAME ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME";
List<Node[]> memberUris3 = sparql(query, true);
List<Node[]> result3 = applyRestrictions(memberUris3, restrictions);
// Add all of result3 to result
first = true;
for (Node[] nodes : result3) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
/*
* ... and Literal values / values that do not serve a skos:notation.
*/
query = STANDARDPREFIX
+ "Select distinct \""
+ CATALOG_NAME
+ "\" as ?CATALOG_NAME \""
+ SCHEMA_NAME
+ "\" as ?SCHEMA_NAME ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?HIERARCHY_UNIQUE_NAME ?DIMENSION_UNIQUE_NAME as ?LEVEL_UNIQUE_NAME \"0\" as ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME as ?MEMBER_NAME ?MEMBER_UNIQUE_NAME ?MEMBER_UNIQUE_NAME as ?MEMBER_CAPTION \"1\" as ?MEMBER_TYPE \"null\" as ?PARENT_UNIQUE_NAME \"null\" as ?PARENT_LEVEL "
+ askForFrom(true)
+ " where { ?CUBE_NAME qb:component ?compSpec. "
+ "?compSpec qb:dimension ?DIMENSION_UNIQUE_NAME. FILTER NOT EXISTS { ?DIMENSION_UNIQUE_NAME qb:codeList ?HIERARCHY_UNIQUE_NAME. } "
+ "?obs qb:dataSet ?ds. ?ds qb:structure ?CUBE_NAME. ?obs ?DIMENSION_UNIQUE_NAME ?MEMBER_UNIQUE_NAME."
+ " FILTER NOT EXISTS {?MEMBER_UNIQUE_NAME skos:notation ?MEMBER_CAPTION } } "
+ "order by ?CUBE_NAME ?DIMENSION_UNIQUE_NAME ?HIERARCHY_UNIQUE_NAME ?LEVEL_UNIQUE_NAME ?LEVEL_NUMBER ?MEMBER_UNIQUE_NAME";
List<Node[]> memberUris31 = sparql(query, true);
List<Node[]> result31 = applyRestrictions(memberUris31, restrictions);
// Add all of result3 to result
first = true;
for (Node[] nodes : result31) {
if (first) {
first = false;
continue;
}
result.add(nodes);
}
return result;
}
public List<Node[]> getSets(Restrictions restrictions) throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public void rollback() {
// TODO Auto-generated method stub
}
@Override
public List<Node[]> getDatabases(Restrictions restrictions)
throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Node[]> getCatalogs(Restrictions restrictions)
throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Node[]> getSchemas(Restrictions restrictions)
throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Node[]> executeOlapQuery(Cube cube, List<Level> slicesrollups,
List<Position> dices, List<Measure> projections)
throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Node[]> executeOlapQuery(LogicalOlapQueryPlan queryplan)
throws OlapException {
// TODO Auto-generated method stub
return null;
}
@Override
public PhysicalOlapQueryPlan getExecplan()
throws OlapException {
// TODO Auto-generated method stub
return null;
}
// /**
// * Creates a SPARQL result for one specific cell.
// *
// * Example SPARQL query: PREFIX sdmx-measure:
// * <http://purl.org/linked-data/sdmx/2009/measure#> PREFIX dcterms:
// * <http://purl.org/dc/terms/> PREFIX eus:
// * <http://ontologycentral.com/2009/01/eurostat/ns#> PREFIX rdf:
// * <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX qb:
// * <http://purl.org/linked-data/cube#> PREFIX rdfs:
// * <http://www.w3.org/2000/01/rdf-schema#> prefix owl:
// * <http://www.w3.org/2002/07/owl#> prefix skos:
// * <http://www.w3.org/2004/02/skos/core#>
// *
// * select distinct * where { ?obs qb:dataSet ?ds. ?ds qb:structure
// * <http://edgarwrap
// * .ontologycentral.com/archive/921590/0001002105-11-000241#dsd>. ?obs
// * <http://edgarwrap.ontologycentral.com/vocab/edgar#segment>
// * ?segmentMember. ?segmentMemberConcept skos:member
// * <http://edgarwrap.ontologycentral.com/vocab/edgar#segmentRootLevel>.
// * ?segmentMemberConcept skos:notation ?segmentMember. ?obs
// * <http://purl.org/dc/terms/date> ?dateMember. ?dateMemberConcept
// * skos:member <http://purl.org/dc/terms/dateRootLevel>.
// ?dateMemberConcept
// * skos:notation ?dateMember. ?obs
// * <http://purl.org/linked-data/sdmx/2009/measure#obsValue> ?obsValue. }
// * ORDER BY ?segmentMember ?dateMember
// *
// * Issues: How to map the CellSetMetadata and the CellSet? Given we ask
// for
// * each value either with ordinal, coordinates, or positions. Our aim
// should
// * be to have through the SPARQL query for each cell in the CellSet simply
// * one value (or, we can have a bunch of them, but then we need to capture
// * the provenance information). In OLAP4J, getCell return a Cell: A Cell
// can
// * have an object value and object properties which we could look into
// * later. For now, we want to return at least one formattedValue. Of
// course,
// * it would be nice to fill several values at once, but maybe for now, we
// * just query each value separately? We can pre-fetch/calculate and store
// * values, later. In order to query for a value, we need: The Cube (dsd)
// The
// * Dimensions and Members on each axis (dimension property, member concept
// /
// * actual values in the data) If no measure is included, the default
// measure
// * (aggregation function, measure property) If we query a triple store,
// what
// * named graphs to query?
// *
// * @param xmlaOlap4jCellSet
// * @param pos
// *
// * @param cube
// * @param members
// * @param filtermembers
// * - at the moment, we assume that this is a flat list of members
// * that needs to aggregated over
// * @param measure
// * @return
// */
// public Cell getOlapCellResult(LdOlap4jCellSet xmlaOlap4jCellSet, int pos,
// LdOlap4jCube cube, List<Member> members,
// List<Member> filtermembers, Measure measure) {
//
// // Note: The cube is surrounded by square brackets.
// String dsd = LdOlap4jUtil.decodeUriForMdx(cube.getUniqueName()
// .substring(1, cube.getUniqueName().length() - 1));
//
// /*
// * TODO: Some observations (blank nodes) are returned several time,
// * therefore the obs are counted and distincted.
// */
// String query = STANDARDPREFIX + "select distinct ?obs ?obsValue " + " "
// + "where { ";
//
// String measureProperty = LdOlap4jUtil.decodeUriForMdx(measure
// .getUniqueName());
//
// String memberPart = "";
// int i = 0;
// for (Member member : members) {
// String dimensionProperty = LdOlap4jUtil.decodeUriForMdx(member
// .getDimension().getUniqueName());
// String levelConcept = LdOlap4jUtil.decodeUriForMdx(member
// .getLevel().getUniqueName());
// String dimensionValueConcept = LdOlap4jUtil.decodeUriForMdx(member
// .getUniqueName());
// // It can only be either literal or resource: notion, or
// // exactMatch
// String representation = LdOlap4jUtil
// .getSkosPropertyOfDimension(dimensionProperty);
//
// memberPart += "?obs <" + dimensionProperty + "> ?member" + i + "."
// + "<" + dimensionValueConcept + "> skos:member <"
// + levelConcept + ">. " + "<" + dimensionValueConcept + "> "
// + representation + " ?member" + i + " .";
// i++;
// }
//
// String filterpart = "";
// // Depending on the size of the filter, we need to do querying.
// // TODO: Magic number 3 needed?
// if (filtermembers.size() == 0 || filtermembers.size() >= 3) {
// // Add the structure
// filterpart = "?obs qb:dataSet ?ds." + "?ds qb:structure <" + dsd
// + ">." + memberPart;
// // Add the measure
// filterpart += "?obs <" + measureProperty + "> ?obsValue.";
//
// } else if (filtermembers.size() == 1) {
// // If filterpart is 1, then just add it as a next member
// String dimensionProperty = LdOlap4jUtil
// .decodeUriForMdx(filtermembers.get(0).getDimension()
// .getUniqueName());
// String levelConcept = LdOlap4jUtil.decodeUriForMdx(filtermembers
// .get(0).getLevel().getUniqueName());
// String dimensionValue = LdOlap4jUtil.decodeUriForMdx(filtermembers
// .get(0).getUniqueName());
//
// // Add the structure
// filterpart = "?obs qb:dataSet ?ds." + "?ds qb:structure <" + dsd
// + ">." + memberPart;
//
// // It can only be either literal or resource: notion, or
// // exactMatch
// // To make process faster, we ask the skos property.
// String representation = LdOlap4jUtil
// .getSkosPropertyOfDimension(dimensionProperty);
//
// filterpart += "?obs <" + dimensionProperty + "> ?member" + i + "."
// + "<" + dimensionValue + "> skos:member <" + levelConcept
// + ">. " + "<" + dimensionValue + "> " + representation
// + " ?member" + i + " .";
//
// // Add the measure
// filterpart += "?obs <" + measureProperty + "> ?obsValue.";
//
// i++;
// } else if (filtermembers.size() > 1) {
// // If filterpart is >1, then UNION it
// boolean first = true;
// for (Member filtermember : filtermembers) {
//
// String dimensionProperty = LdOlap4jUtil
// .decodeUriForMdx(filtermember.getDimension()
// .getUniqueName());
// String levelConcept = LdOlap4jUtil.decodeUriForMdx(filtermember
// .getLevel().getUniqueName());
// String dimensionValue = LdOlap4jUtil
// .decodeUriForMdx(filtermember.getUniqueName());
//
// // It can only be either literal or resource: notion, or
// // exactMatch
// String union;
// if (first) {
// union = "";
// } else {
// union = " UNION ";
// }
// first = false;
//
// /*
// * if first union = ""
// *
// * { + dataset + memberpart + filtermemberpart + measurepart}
// *
// * else
// *
// * UNION { + dataset + memberpart + filtermemberpart +
// * measurepart}
// */
//
// // Only skos properties
// // To make process faster, we ask the skos property.
// String representation = LdOlap4jUtil
// .getSkosPropertyOfDimension(dimensionProperty);
//
// // Add the union part and the structure
// filterpart += union + " { " + " ?obs qb:dataSet ?ds."
// + "?ds qb:structure <" + dsd + ">." + memberPart;
//
// filterpart += " ?obs <" + dimensionProperty + "> ?member" + i
// + "." + "<" + dimensionValue + "> skos:member <"
// + levelConcept + ">. " + "<" + dimensionValue + "> "
// + representation + " ?member .";
// // Add the measure
// filterpart += "?obs <" + measureProperty + "> ?obsValue.";
// filterpart += " } ";
//
// i++;
// }
// }
//
// query += filterpart;
//
// query += "}";
//
// // These kinds of queries are too often, therefore no cache.
// List<Node[]> cellNodes = sparql(query, false);
//
// if (cellNodes.size() == 2) {
// // 0 = obs 1 = value
// return new LdOlap4jCell(xmlaOlap4jCellSet, pos, null,
// cellNodes.get(1)[1].toString(),
// Collections.<Property, Object> emptyMap());
// } else {
// return new LdOlap4jCell(xmlaOlap4jCellSet, pos, null,
// (cellNodes.size() - 1) + " values",
// Collections.<Property, Object> emptyMap());
// }
// }
// /**
// * In order to have a quick estimation of how many values we will have, we
// * create a map of all possible dimension members of the levels of the
// * mentioned members to the number of observations. Here, we do not take
// * filter into account, therefore, we can have a possible occurrence, and
// * later, after filtering, we will actually have zero value.
// *
// * @param xmlaOlap4jCellSet
// * @param pos
// * @param cube
// * @param members
// * @param filtermembers
// * @param measure
// * @return
// */
// public Map<Integer, Integer> getOlapResultEstimation(
// LdOlap4jCellSet xmlaOlap4jCellSet, int pos, LdOlap4jCube cube,
// List<Member> members, List<Member> filtermembers, Measure measure) {
//
// // Note: The cube is surrounded by square brackets.
// String dsd = LdOlap4jUtil.decodeUriForMdx(cube.getUniqueName()
// .substring(1, cube.getUniqueName().length() - 1));
//
// String memberPart = "";
// String conceptSelects = "";
// int i = 0;
// for (Member member : members) {
//
// String dimensionProperty = LdOlap4jUtil.decodeUriForMdx(member
// .getDimension().getUniqueName());
// String levelConcept = LdOlap4jUtil.decodeUriForMdx(member
// .getLevel().getUniqueName());
//
// // To make process faster, we ask the skos property.
// String representation = LdOlap4jUtil
// .getSkosPropertyOfDimension(dimensionProperty);
//
// memberPart += "?obs <" + dimensionProperty + "> ?member" + i + "."
// + "?concept" + i + " skos:member <" + levelConcept
// + ">. ?concept" + i + " " + representation + " ?member" + i
// + " .";
//
// conceptSelects += " ?concept" + i;
// i++;
// }
//
// /*
// * TODO: Some observations (blank nodes) are returned several time,
// * therefore the obs are counted and distincted.
// */
// String query = STANDARDPREFIX + "select " + conceptSelects
// + " count(distinct ?obs) " + " " + "where { ";
//
// query += "?obs qb:dataSet ?ds." + "?ds qb:structure <" + dsd + ">.";
//
// query += memberPart;
//
// String measureProperty = LdOlap4jUtil.decodeUriForMdx(measure
// .getUniqueName());
//
// query += "?obs <" + measureProperty + "> ?obsValue.";
//
// query += "} ";
//
// List<Node[]> cellNodes = sparql(query, true);
//
// HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
// boolean first = true;
// for (Node[] nodes : cellNodes) {
// if (first) {
// // The first is not interesting
// first = false;
// continue;
// }
// // Take the number of hierarchies
// String memberConcat = "";
// for (int j = 0; j < nodes.length - 1; j++) {
// memberConcat += nodes[j].toString();
// }
// // Measure also concat!
// memberConcat += measure.getUniqueName();
// // Value
// String value = nodes[nodes.length - 1].toString();
// if (value == null || value.equals("null")) {
// map.put(memberConcat.hashCode(), 0);
// } else {
// map.put(memberConcat.hashCode(), new Integer(value));
// }
// }
//
// return map;
// }
}