/* * 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.xenei.jdbc4sparql.sparql.builders; import java.sql.DatabaseMetaData; import java.sql.Types; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.xenei.jdbc4sparql.iface.TypeConverter; import org.xenei.jdbc4sparql.impl.rdf.RdfCatalog; import org.xenei.jdbc4sparql.impl.rdf.RdfColumnDef; import org.xenei.jdbc4sparql.impl.rdf.RdfSchema; import org.xenei.jdbc4sparql.impl.rdf.RdfTable; import org.xenei.jdbc4sparql.impl.rdf.RdfTableDef; import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.util.iterator.Map1; import com.hp.hpl.jena.util.iterator.WrappedIterator; /** * A simple builder that builds tables for all subjects of [?x a rdfs:Class] * triples. Columns for the tables are created from all predicates of all * instances of the class. */ public class SimpleBuilder implements SchemaBuilder { public static final String BUILDER_NAME = "Simple_Builder"; public static final String DESCRIPTION = "A simple schema builder that builds tables based on RDFS Class names"; // Params: namespace. protected static final String TABLE_QUERY = " prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>" + " SELECT ?tName WHERE { ?tName a rdfs:Class . }"; // Params: class resource, namespace protected static final String COLUMN_QUERY = "SELECT DISTINCT ?cName " + " WHERE { ?instance a <%s> ; ?cName [] ; }"; private static final String TABLE_SEGMENT = "%1$s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <%2$s> ."; protected static final String COLUMN_SEGMENT = "%1$s <%3$s> %2$s ."; public SimpleBuilder() { } protected Map<String, String> addColumnDefs(final RdfCatalog catalog, final RdfTableDef.Builder tableDefBuilder, final Resource tName, final String tableQuerySegment) { final Model model = catalog.getResource().getModel(); final Map<String, String> colNames = new LinkedHashMap<String, String>(); final List<QuerySolution> solns = catalog.executeQuery(String.format( SimpleBuilder.COLUMN_QUERY, tName)); for (final QuerySolution soln : solns) { final RdfColumnDef.Builder builder = new RdfColumnDef.Builder(); final Resource cName = soln.getResource("cName"); final String columnQuerySegment = String.format( SimpleBuilder.COLUMN_SEGMENT, "%1$s", "%2$s", cName.getURI()); // might be a duplicate name if (colNames.containsKey(cName.getLocalName())) { int i = 2; while (colNames.containsKey(cName.getLocalName() + i)) { i++; } colNames.put(cName.getLocalName() + i, columnQuerySegment); } else { colNames.put(cName.getLocalName(), columnQuerySegment); } final int scale = calculateSize(catalog, tableQuerySegment, columnQuerySegment); builder.setType(Types.VARCHAR) .setNullable(DatabaseMetaData.columnNullable) .setScale(scale).setReadOnly(true); tableDefBuilder.addColumnDef(builder.build(model)); } return colNames; } protected int calculateSize(final RdfCatalog catalog, final String tableQS, final String columnQS) { final String queryStr = String.format( "SELECT distinct ?col WHERE { %s %s }", String.format(tableQS, "?tbl"), String.format(columnQS, "?tbl", "?col")); final List<QuerySolution> results = catalog.executeQuery(queryStr); final Iterator<Integer> iter = WrappedIterator.create( results.iterator()).mapWith(new Map1<QuerySolution, Integer>() { @Override public Integer map1(final QuerySolution o) { final RDFNode node = o.get("col"); if (node == null) { return 0; } if (node.isLiteral()) { return TypeConverter.getJavaValue(node.asLiteral()) .toString().length(); } return node.toString().length(); } }); int retval = 0; while (iter.hasNext()) { final Integer i = iter.next(); if (retval < i) { retval = i; } } return retval; } @Override public Set<RdfTable> getTables(final RdfSchema schema) { final RdfCatalog catalog = schema.getCatalog(); final Model model = schema.getResource().getModel(); final HashSet<RdfTable> retval = new HashSet<RdfTable>(); final List<QuerySolution> solns = catalog .executeQuery(SimpleBuilder.TABLE_QUERY); for (final QuerySolution soln : solns) { final Resource tName = soln.getResource("tName"); final RdfTableDef.Builder builder = new RdfTableDef.Builder(); final String tableQuerySegment = String.format( SimpleBuilder.TABLE_SEGMENT, "%1$s", tName.getURI()); final Map<String, String> colNames = addColumnDefs(catalog, builder, tName, tableQuerySegment); if (colNames.size() > 0) { final RdfTableDef tableDef = builder.build(model); final RdfTable.Builder tblBuilder = new RdfTable.Builder() .setTableDef(tableDef) .addQuerySegment(tableQuerySegment) .setName(tName.getLocalName()).setSchema(schema) .setRemarks("created by " + SimpleBuilder.BUILDER_NAME); if (colNames.keySet().size() != tableDef.getColumnCount()) { throw new IllegalArgumentException( String.format( "There must be %s column names, %s provided", tableDef.getColumnCount(), colNames .keySet().size())); } final Iterator<String> iter = colNames.keySet().iterator(); int i = 0; while (iter.hasNext()) { final String cName = iter.next(); tblBuilder .setColumn(i, cName) .getColumn(i) .addQuerySegment(colNames.get(cName)) .setRemarks( "created by " + SimpleBuilder.BUILDER_NAME); i++; } retval.add(tblBuilder.build(model)); } } return retval; } }