package org.xenei.jdbc4sparql.impl.rdf; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.xenei.jdbc4sparql.iface.Catalog; import org.xenei.jdbc4sparql.iface.Column; import org.xenei.jdbc4sparql.iface.NameFilter; import org.xenei.jdbc4sparql.iface.Schema; import org.xenei.jdbc4sparql.iface.Table; import org.xenei.jdbc4sparql.iface.name.TableName; import org.xenei.jdbc4sparql.impl.NameUtils; import org.xenei.jdbc4sparql.sparql.SparqlQueryBuilder; import org.xenei.jdbc4sparql.sparql.SparqlResultSet; import org.xenei.jdbc4sparql.sparql.items.QueryTableInfo; import org.xenei.jdbc4sparql.sparql.parser.SparqlParser; import org.xenei.jena.entities.EntityManager; import org.xenei.jena.entities.EntityManagerFactory; import org.xenei.jena.entities.EntityManagerRequiredException; import org.xenei.jena.entities.MissingAnnotation; import org.xenei.jena.entities.ResourceWrapper; import org.xenei.jena.entities.annotations.Predicate; import org.xenei.jena.entities.annotations.Subject; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFList; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.shared.Lock; import com.hp.hpl.jena.vocabulary.RDFS; @Subject(namespace = "http://org.xenei.jdbc4sparql/entity/Table#") public class RdfTable extends RdfNamespacedObject implements Table, ResourceWrapper { public static class Builder implements Table { public static RdfTable fixupSchema(final RdfSchema schema, final RdfTable table) { table.schema = schema; final Property p = ResourceFactory.createProperty( ResourceBuilder.getNamespace(RdfSchema.class), "tables"); schema.getResource().addProperty(p, table.getResource()); return table; } private String remarks = ""; private RdfTableDef tableDef; private String name; private RdfSchema schema; private RdfColumn.Builder[] columns; private String type = "SPARQL TABLE"; private final Class<? extends RdfTable> typeClass = RdfTable.class; private final List<String> querySegments = new ArrayList<String>(); /** * Add a string format where $1%s is the table name. * * @param querySegment * @return */ public Builder addQuerySegment(final String querySegment) { querySegments.add(querySegment); return this; } public RdfTable build(final Model model) { checkBuildState(); final String fqName = getFQName(); final ResourceBuilder builder = new ResourceBuilder(model); final EntityManager entityManager = EntityManagerFactory .getEntityManager(); Resource table = null; if (builder.hasResource(fqName)) { table = builder.getResource(fqName, typeClass); } else { table = builder.getResource(fqName, typeClass); table.addLiteral(RDFS.label, name); table.addProperty(builder.getProperty(typeClass, "tableDef"), tableDef.getResource()); table.addLiteral(builder.getProperty(typeClass, "type"), type); table.addLiteral(builder.getProperty(typeClass, "remarks"), StringUtils.defaultString(remarks)); if (!querySegments.isEmpty()) { final Property querySegmentProp = builder.getProperty( RdfTable.class, "querySegmentFmt"); table.addLiteral(querySegmentProp, getQuerySegmentFmt()); querySegments.clear(); } schema.getResource().addProperty( builder.getProperty(RdfSchema.class, "tables"), table); } try { final RdfTable retval = entityManager.read(table, typeClass); retval.schema = schema; // add the column names RDFList lst = null; for (final RdfColumn.Builder bldr : columns) { bldr.setTable(retval); final RdfColumn col = bldr.build(model); if (retval.columns == null) { retval.columns = new ArrayList<Column>(); } retval.columns.add(col); if (lst == null) { lst = model.createList().with(col.getResource()); } else { lst.add(col.getResource()); } } final Property p = builder.getProperty(typeClass, "column"); table.addProperty(p, lst); return retval; } catch (final MissingAnnotation e) { throw new RuntimeException(e); } } private void checkBuildState() { if (StringUtils.isBlank(name)) { throw new IllegalStateException("Name must be set"); } if (StringUtils.isBlank(type)) { throw new IllegalStateException("Type must be set"); } if (schema == null) { throw new IllegalStateException("schema must be set"); } for (int i = 0; i < columns.length; i++) { if (columns[i] == null) { throw new IllegalStateException(String.format( "column %s must be set", i)); } } if (querySegments.size() == 0) { querySegments.add("# no query segments provided"); } } @Override public void delete() { throw new UnsupportedOperationException(); } @Override public NameFilter<Column> findColumns(final String columnNamePattern) { return new NameFilter<Column>(columnNamePattern, getColumns()); } @Override public Catalog getCatalog() { return getSchema().getCatalog(); } @Override public RdfColumn.Builder getColumn(final int idx) { return columns[idx]; } @Override public Column getColumn(final String name) { for (final Column c : columns) { if (c.getName().equals(name)) { return c; } } return null; } @Override public int getColumnCount() { return tableDef.getColumnCount(); } @Override public int getColumnIndex(final Column column) { final String colName = NameUtils.getDBName(column); for (int i = 0; i < columns.length; i++) { if (colName.equals(NameUtils.getDBName(columns[i]))) { return i; } } return -1; } @Override public int getColumnIndex(final String name) { for (int i = 0; i < columns.length; i++) { if (name.equals(columns[i].getName())) { return i; } } return -1; } @Override public List<Column> getColumnList() { return new ArrayList<Column>(Arrays.asList(columns)); } @Override public Iterator<Column> getColumns() { return getColumnList().iterator(); } public String getFQName() { final StringBuilder sb = new StringBuilder() .append(schema.getResource().getURI()).append(" ") .append(name).append(" ").append(getQuerySegmentFmt()); return String .format("%s/instance/N%s", ResourceBuilder .getFQName(RdfTable.class), UUID.nameUUIDFromBytes(sb.toString().getBytes()) .toString()); } @Override public TableName getName() { return getSchema().getName().getTableName(name); } @Override public String getQuerySegmentFmt() { final String eol = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(); if (!querySegments.isEmpty()) { for (final String seg : querySegments) { sb.append(seg).append(eol); } } else { sb.append("{ # no query statements provided }").append(eol); } return sb.toString(); } @Override public String getRemarks() { return remarks; } @Override public Schema getSchema() { return schema; } @Override public String getSPARQLName() { return NameUtils.getSPARQLName(this); } @Override public String getSQLName() { return NameUtils.getDBName(this); } @Override public Table getSuperTable() { throw new UnsupportedOperationException(); } @Override public RdfTableDef getTableDef() { return tableDef; } @Override public String getType() { return type; } @Override public boolean hasQuerySegments() { return !querySegments.isEmpty(); } public Builder setColumn(final int idx, final String name) { if (tableDef == null) { throw new IllegalStateException( "TableDef must be specified before defining columns"); } if ((idx < 0) || (idx >= columns.length)) { throw new IllegalArgumentException(String.format( "index '%s' must be between 0 and %s inclusive", idx, columns.length - 1)); } final RdfColumn.Builder builder = new RdfColumn.Builder() .setColumnDef(tableDef.getColumnDef(idx)).setName(name) .setTable(this); columns[idx] = builder; return this; } public Builder setColumns(final Collection<String> colNames) { if (colNames.size() != tableDef.getColumnCount()) { throw new IllegalArgumentException(String.format( "There must be %s column names, %s provided", tableDef.getColumnCount(), colNames.size())); } final Iterator<String> iter = colNames.iterator(); int i = 0; while (iter.hasNext()) { setColumn(i, iter.next()); i++; } return this; } public Builder setName(final String name) { this.name = name; return this; } public Builder setRemarks(final String remarks) { this.remarks = remarks; return this; } public Builder setSchema(final RdfSchema schema) { this.schema = schema; return this; } public Builder setTableDef(final RdfTableDef tableDef) { this.tableDef = tableDef; this.columns = new RdfColumn.Builder[tableDef.getColumnCount()]; return this; } public Builder setType(final String type) { this.type = type; return this; } } private List<Column> columns; private RdfTableDef tableDef; private RdfSchema schema; private SparqlQueryBuilder queryBuilder; private TableName tableName; @Override public void delete() { final Resource tbl = getResource(); final Model model = tbl.getModel(); // final ResourceBuilder builder = new ResourceBuilder(model); model.enterCriticalSection(Lock.WRITE); try { // preserve the column objects final List<Column> cols = getColumnList(); final Property p = model.createProperty( ResourceBuilder.getNamespace(RdfTable.class), "column"); tbl.getRequiredProperty(p).getResource().as(RDFList.class) .removeList(); // delete the column objects for (final Column col : cols) { ((RdfColumn) col).delete(); } model.remove(null, null, tbl); model.remove(tbl, null, null); } finally { model.leaveCriticalSection(); } } @Override public NameFilter<Column> findColumns(final String columnNamePattern) { return new NameFilter<Column>(columnNamePattern, getColumnList()); } @Override public RdfCatalog getCatalog() { return getSchema().getCatalog(); } @Override public Column getColumn(final int idx) { return getColumnList().get(idx); } @Override public Column getColumn(final String name) { for (final Column col : getColumnList()) { if (col.getName().getShortName().equals(name)) { return col; } } return null; } @Override public int getColumnCount() { return getColumnList().size(); } @Override public int getColumnIndex(final Column column) { return getColumnList().indexOf(column); } /** * Returns the column index for hte name or -1 if not found * * @param columnName * The name to search for * @return the column index (0 based) or -1 if not found. */ @Override public int getColumnIndex(final String columnName) { final List<Column> cols = getColumnList(); for (int i = 0; i < cols.size(); i++) { if (cols.get(i).getName().equals(columnName)) { return i; } } return -1; } @Override public synchronized List<Column> getColumnList() { if (columns == null) { readTableDef(); // force read of table def. final EntityManager entityManager = EntityManagerFactory .getEntityManager(); columns = new ArrayList<Column>(); final Resource tbl = this.getResource(); final Model model = tbl.getModel(); final Property p = model.createProperty( ResourceBuilder.getNamespace(RdfTable.class), "column"); final List<RDFNode> resLst = tbl.getRequiredProperty(p) .getResource().as(RDFList.class).asJavaList(); for (final RDFNode n : resLst) { try { final RdfColumn col = entityManager .read(n, RdfColumn.class); columns.add(RdfColumn.Builder.fixupTable(this, col)); } catch (final MissingAnnotation e) { throw new RuntimeException(e); } } } return columns; } @Override public Iterator<Column> getColumns() { return getColumnList().iterator(); } public RdfKey getKey() { RdfKey key = readTableDef().getSortKey(); if (key == null) { key = readTableDef().getPrimaryKey(); } return key; } @Override public TableName getName() { if (tableName == null) { tableName = getSchema().getName().getTableName(getShortName()); } return tableName; } private Query getQuery(final Map<String, Catalog> catalogs, final SparqlParser parser) throws SQLException { if (queryBuilder == null) { final RdfCatalog catalog = getCatalog(); queryBuilder = new SparqlQueryBuilder(catalogs, parser, catalog, schema); final QueryTableInfo tableInfo = queryBuilder.addTable(getName(), getName(), SparqlQueryBuilder.REQUIRED); queryBuilder.setSegmentCount(); queryBuilder.addDefinedColumns(); queryBuilder.addTableColumns(tableInfo); final RdfKey key = getKey(); if (key != null) { queryBuilder.setKey(key); } else { if (readTableDef().isDistinct()) { queryBuilder.setDistinct(); } } } return queryBuilder.build(); } @Override @Predicate(impl = true) public String getQuerySegmentFmt() { throw new EntityManagerRequiredException(); } @Override @Predicate(impl = true) public String getRemarks() { throw new EntityManagerRequiredException(); } @Override @Predicate(impl = true) public Resource getResource() { throw new EntityManagerRequiredException(); } public SparqlResultSet getResultSet(final Map<String, Catalog> catalogs, final SparqlParser parser) throws SQLException { return new SparqlResultSet(this, getQuery(catalogs, parser)); } @Override public RdfSchema getSchema() { return schema; } @Predicate(impl = true, namespace = "http://www.w3.org/2000/01/rdf-schema#", name = "label") public String getShortName() { throw new EntityManagerRequiredException(); } @Override public String getSPARQLName() { return NameUtils.getSPARQLName(this); } @Override public String getSQLName() { return NameUtils.getDBName(this); } @Override public RdfTable getSuperTable() { final RdfTableDef superTableDef = readTableDef().getSuperTableDef(); if (superTableDef != null) { final Builder tableBuilder = new Builder().setTableDef( superTableDef).setSchema(getSchema()); return tableBuilder.build(getResource().getModel()); } return null; } @Override @Predicate(impl = true) public RdfTableDef getTableDef() { throw new EntityManagerRequiredException(); } @Override @Predicate(impl = true) public String getType() { throw new EntityManagerRequiredException(); } @Override public boolean hasQuerySegments() { return true; } private RdfTableDef readTableDef() { if (tableDef == null) { tableDef = getTableDef(); } return tableDef; } @Override public String toString() { return getName().toString(); } }