/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.jena.jdbc.results.metadata;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.Iterator;
import org.apache.jena.atlas.iterator.PeekIterator;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.jdbc.JdbcCompatibility;
import org.apache.jena.jdbc.results.JenaResultSet;
import org.apache.jena.jdbc.results.TripleIteratorResults;
import org.apache.jena.jdbc.results.metadata.columns.ColumnInfo;
import org.apache.jena.jdbc.results.metadata.columns.SparqlColumnInfo;
import org.apache.jena.jdbc.results.metadata.columns.StringColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Result set metadata for {@link TripleIteratorResults} instances
*
*/
public class TripleResultsMetadata extends JenaResultsMetadata {
private static final Logger LOGGER = LoggerFactory.getLogger(TripleResultsMetadata.class);
/**
* Constant for the default subject column label
*/
public static final String COLUMN_LABEL_SUBJECT = "Subject";
/**
* Constant for the default predicate column label
*/
public static final String COLUMN_LABEL_PREDICATE = "Predicate";
/**
* Constant for the default object column label
*/
public static final String COLUMN_LABEL_OBJECT = "Object";
/**
* Constant for the subject column index (assuming no columns are omitted)
*/
public static final int COLUMN_INDEX_SUBJECT = 1;
/**
* Constant for the predicate column index (assuming no columns are omitted)
*/
public static final int COLUMN_INDEX_PREDICATE = 2;
/**
* Constant for the object column index (assuming no columns are omitted)
*/
public static final int COLUMN_INDEX_OBJECT = 3;
/**
* Constant for the number of columns in triple results
*/
public static final int NUM_COLUMNS = 3;
private PeekIterator<Triple> ts;
private String subjColumn, predColumn, objColumn;
/**
* Gets the columns for CONSTRUCT/DESCRIBE results
*
* @param results
* Results
* @param ts
* Underlying triples
*
* @return Column Information
* @throws SQLException
*/
private static ColumnInfo[] makeColumns(JenaResultSet results, PeekIterator<Triple> ts) throws SQLException {
return makeColumns(results, ts, COLUMN_LABEL_SUBJECT, COLUMN_LABEL_PREDICATE, COLUMN_LABEL_OBJECT);
}
/**
* Gets the columns for CONSTRUCT/DESCRIBE results
*
* @param results
* Results
* @param ts
* Underlying triples
* @param subjLabel
* Label for subject column, use {@code null} to omit the subject
* column
* @param predLabel
* Label for predicate column, use {@code null} to omit the
* predicate column
* @param objLabel
* Label for object column, use {@code null} to omit the object
* column
*
* @return Column Information
* @throws SQLException
*/
private static ColumnInfo[] makeColumns(JenaResultSet results, PeekIterator<Triple> ts, String subjLabel, String predLabel,
String objLabel) throws SQLException {
int numColumns = 0;
if (subjLabel != null)
numColumns++;
if (predLabel != null)
numColumns++;
if (objLabel != null)
numColumns++;
ColumnInfo[] columns = new ColumnInfo[numColumns];
// Figure out column names
String[] names = new String[numColumns];
names[0] = subjLabel != null ? subjLabel : (predLabel != null ? predLabel : objLabel);
if (numColumns > 1) {
names[1] = subjLabel != null && predLabel != null ? predLabel : objLabel;
}
if (numColumns == 3) {
names[2] = objLabel;
}
int level = JdbcCompatibility.normalizeLevel(results.getJdbcCompatibilityLevel());
boolean columnsAsStrings = JdbcCompatibility.shouldTypeColumnsAsString(level);
boolean columnsDetected = JdbcCompatibility.shouldDetectColumnTypes(level);
Triple t = null;
Node[] values = new Node[numColumns];
if (columnsDetected) {
if (ts.hasNext()) {
// Need to peek the first Triple and grab appropriate nodes
t = ts.peek();
if (numColumns == NUM_COLUMNS) {
values[0] = t.getSubject();
values[1] = t.getPredicate();
values[2] = t.getObject();
} else {
values[0] = subjLabel != null ? t.getSubject() : (predLabel != null ? t.getPredicate() : t.getObject());
if (numColumns > 1) {
values[1] = subjLabel != null && predLabel != null ? t.getPredicate() : t.getObject();
}
}
} else {
// If we were supposed to detect columns but there is no data
// available then we will just fallback to typing everything as
// strings
columnsAsStrings = true;
columnsDetected = false;
}
}
for (int i = 0; i < columns.length; i++) {
if (!columnsAsStrings && !columnsDetected) {
// Low compatibility, report columns as being typed as
// JAVA_OBJECT with ARQ Node as the column class
columns[i] = new SparqlColumnInfo(names[i], Types.JAVA_OBJECT, columnNoNulls);
LOGGER.info("Low JDBC compatibility, column " + names[i] + " is being typed as Node");
} else if (columnsAsStrings) {
// Medium compatibility, report columns as being typed as
// NVARChar with String as the column class
columns[i] = new StringColumn(names[i], columnNoNulls);
LOGGER.info("Medium JDBC compatibility, column " + names[i] + " is being typed as String");
} else if (columnsDetected) {
// High compatibility, detect columns types based on first row
// of results
columns[i] = JdbcCompatibility.detectColumnType(names[i], values[i], false);
LOGGER.info("High compatibility, column " + names[i] + " was detected as being of type "
+ columns[i].getClassName());
} else {
throw new SQLFeatureNotSupportedException("Unknown JDBC compatibility level was set");
}
}
return columns;
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param results
* Result Set
* @param ts
* Triple iterator
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(JenaResultSet results, PeekIterator<Triple> ts) throws SQLException {
super(results, makeColumns(results, ts));
this.subjColumn = COLUMN_LABEL_SUBJECT;
this.predColumn = COLUMN_LABEL_PREDICATE;
this.objColumn = COLUMN_LABEL_OBJECT;
this.ts = ts;
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param results
* Result Set
* @param ts
* Triple iterator
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(JenaResultSet results, Iterator<Triple> ts) throws SQLException {
this(results, PeekIterator.create(ts));
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param results
* Result Set
* @param ts
* Triple iterator
* @param subjLabel
* Label for subject column, use {@code null} to omit the subject
* column
* @param predLabel
* Label for predicate column, use {@code null} to omit the
* predicate column
* @param objLabel
* Label for object column, use {@code null} to omit the object
* column
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(JenaResultSet results, PeekIterator<Triple> ts, String subjLabel, String predLabel,
String objLabel) throws SQLException {
super(results, makeColumns(results, ts, subjLabel, predLabel, objLabel));
this.subjColumn = subjLabel;
this.predColumn = predLabel;
this.objColumn = objLabel;
this.ts = ts;
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param results
* Result Set
* @param ts
* Triple iterator
* @param subjLabel
* Label for subject column, use {@code null} to omit the subject
* column
* @param predLabel
* Label for predicate column, use {@code null} to omit the
* predicate column
* @param objLabel
* Label for object column, use {@code null} to omit the object
* column
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(JenaResultSet results, Iterator<Triple> ts, String subjLabel, String predLabel, String objLabel)
throws SQLException {
this(results, PeekIterator.create(ts), subjLabel, predLabel, objLabel);
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param metadata
* Metadata
* @param subjLabel
* Label for subject column, use {@code null} to omit the subject
* column
* @param predLabel
* Label for predicate column, use {@code null} to omit the
* predicate column
* @param objLabel
* Label for object column, use {@code null} to omit the object
* column
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(TripleResultsMetadata metadata, String subjLabel, String predLabel, String objLabel)
throws SQLException {
this(metadata.getJenaResultSet(), metadata.ts, subjLabel, predLabel, objLabel);
}
/**
* Creates new results metadata for triple (CONSTRUCT/DESCRIBE) results
*
* @param metadata
* Metadata
* @param columns
* Column Information
* @throws SQLException
* Thrown if the metadata cannot be created
*/
public TripleResultsMetadata(TripleResultsMetadata metadata, ColumnInfo[] columns) throws SQLException {
super(metadata.getJenaResultSet(), columns);
if (metadata.getColumnCount() != columns.length)
throw new SQLException(
"Must provide same number of columns as in original metadata, to filter down included columns use the TripleResultsMetadata(TripleResultsMetadata, String, String, String) constructor first");
this.subjColumn = metadata.getSubjectColumnLabel();
this.predColumn = metadata.getPredicateColumnLabel();
this.objColumn = metadata.getObjectColumnLabel();
}
/**
* Gets the subject column label
*
* @return Column label or {@code null} if the column is omitted
*/
public String getSubjectColumnLabel() {
return this.subjColumn;
}
/**
* Gets the predicate column label
*
* @return Column label or {@code null} if the column is omitted
*/
public String getPredicateColumnLabel() {
return this.predColumn;
}
/**
* Gets the object column label
*
* @return Column label or {@code null} if the column is omitted
*/
public String getObjectColumnLabel() {
return this.objColumn;
}
}