/** * 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; } }