package org.aksw.sparqlify.core.domain.input; import java.io.ByteArrayOutputStream; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.aksw.jena_sparql_api.restriction.RestrictionManagerImpl; import org.aksw.jena_sparql_api.utils.QuadPatternUtils; import org.aksw.jena_sparql_api.utils.QuadUtils; import org.aksw.jena_sparql_api.views.IViewDef; import org.aksw.jena_sparql_api.views.RestrictedExpr; import org.aksw.jena_sparql_api.views.VarDefinition; import org.aksw.sparqlify.algebra.sql.nodes.SqlOp; import org.aksw.sparqlify.algebra.sql.nodes.SqlOpQuery; import org.aksw.sparqlify.algebra.sql.nodes.SqlOpTable; import org.aksw.sparqlify.core.algorithms.MappingOpsImpl; import org.apache.jena.atlas.io.IndentedWriter; import org.apache.jena.riot.out.SinkQuadBracedOutput; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.core.QuadPattern; import org.apache.jena.sparql.core.Var; /** * A view definition is comprised of * - A name * - A template (a set of quad patterns) * - A mapping * - A set of references to variables declared in other view definitions. * * * Here are some notes on the references: * * Create View view_person { * ?s a Person * } * With * ?s = uri(name) [prefixes = {ns1, ns2}] * From * people_table; * * * Create View employee_to_dept As Construct { * ?p :worksIn ?d * } * References * ppl: view_person On this.person_id = that.id * depts: ... // Reference to the dept view on some join condition * With * ?p = ref(ppl, ?s) // Syntactic sugar for the following line: * ?p_resolved = uri(ppl.name) [prefixes=...] // We now have a qualified column reference and the constraints carry over * ?d = ref(depts, ?s) * From * p2d_table; * * Issue: (Q = Question, T = thought, R = resolution) * - Q: Nested refs: How to treat cases where a view V hase a ref(refName, ?var) which refers to another ref, e.g. ?x = ref(someRef.someNestedRef, ?x) * T: Essentielly it should work somehow like this: for any view instance of V, we would keep track of a (list of) unresolved references; the nested ones would be * simply added. * * * When creating view instances, we now have to keep track of which refereences have been resolved. * If a variable is not bound in a varbinding, then its references do not need to be resolved. * Conversely: Each bound view variable's references are added to the view instance's list of unresolved references. * Also, for each view instance variable we need to deal with the qualified column names. * Not sure where to deal with them best. * * Initially unresolved refs for the employee_to_dept view are ppl and depts. * * * * @author Claus Stadler <cstadler@informatik.uni-leipzig.de> * */ public class ViewDefinition implements IViewDef { public static final ViewDefinition emptyViewDefinition = new ViewDefinition("emptyView", new QuadPattern(), null, MappingOpsImpl.createEmptyMapping(), null); private String name; // Note: all quads in the template must (should?) be composed of variables only // Constants and expressions are associated to a variable in the mapping // object. private QuadPattern template; private Mapping mapping; @Override public Set<Var> getVarsMentioned() { Set<Var> result = QuadPatternUtils.getVarsMentioned(template); return result; } /** * Mapping from reference names to other's views logical table on a given join condition * Usually used for mapping foreign-key relations. * * Note: ColumnReferences can be qualified with the name of the reference. */ private Map<String, ViewReference> viewReferences; // References to variables declaced in other views. Useful for efficient // mapping table handling, as self join elimination can be applied. // Corresponds to R2RML's rr:join. //private Map<Var, VarReference> viewReferences = new HashMap<Var, VarReference>(); // Restrictions on the variables (rather than on their defining expressions) // TODO Implement this again private RestrictionManagerImpl varRestrictions; // The source can point to an arbitrary object from // which this view definition was derived. // Mainly intended for pointing back to to the syntactic // construct this view definition was created from in order to be // able to provide better feedback to the user if problems are // encountered. // // (source can e.g. be an object representing a Sparqlify-ML or R2R-ML // definition). private Object source; public ViewDefinition(String name, QuadPattern template, Map<String, ViewReference> viewReferences, Mapping mapping, Object source) { this(name, template, viewReferences, mapping, null, source); /* this.name = name; this.template = template; this.mapping = mapping; this.viewReferences = viewReferences; this.source = source; */ } public ViewDefinition(String name, QuadPattern template, Map<String, ViewReference> viewReferences, Mapping mapping, RestrictionManagerImpl varRestrictions, Object source) { this.name = name; this.template = template; this.mapping = mapping; this.viewReferences = viewReferences; this.varRestrictions = varRestrictions; this.source = source; } public String getName() { return name; } public QuadPattern getTemplate() { return template; } public Mapping getMapping() { return mapping; } public Map<String, ViewReference> getViewReferences() { return viewReferences; } public Object getSource() { return source; } public ViewDefinition copyRenameVars(Map<Var, Var> oldToNew) { QuadPattern newTemplate = QuadUtils.copySubstitute(this.template, oldToNew); VarDefinition varDef = mapping.getVarDefinition().copyRenameVars(oldToNew); Mapping m = new Mapping(varDef, mapping.getSqlOp()); ViewDefinition result = new ViewDefinition(name, newTemplate, viewReferences, m, this); return result; } public RestrictionManagerImpl getVarRestrictions() { return varRestrictions; } public void write(IndentedWriter writer) { //Head writer.println("Create View " + name + " As"); writer.incIndent(); writer.println("Construct"); //writer.incIndent(); SinkQuadBracedOutput sink = new SinkQuadBracedOutput(writer, null); sink.open(); //sink.send(template); //Map<Node, Set<Quad>> nodeToQuads = QuadUtils.partitionByGraph(template); // Template for(Quad quad : template) { sink.send(quad); //writer.println("" + quad); } sink.close(); writer.println(" "); //writer.decIndent(); // writer.println("}"); // With if(!mapping.getVarDefinition().isEmpty()) { writer.println("With"); writer.incIndent(); for(Entry<Var, RestrictedExpr> entry : mapping.getVarDefinition().getMap().entries()) { Var var = entry.getKey(); RestrictedExpr rexpr = entry.getValue(); //writer.println(var + " = " + rexpr.getExpr() + "; Constraints " + rexpr.getRestrictions()); writer.println(var + " = " + rexpr.getExpr()); //if(rexpr.getRestrictions().) //+ " Constraints " + rexpr.getRestrictions()); } writer.decIndent(); } // From SqlOp op = mapping.getSqlOp(); if(op != null) { writer.println("From"); writer.incIndent(); if(op instanceof SqlOpTable) { SqlOpTable tmp = (SqlOpTable)op; writer.println(tmp.getTableName()); } else if (op instanceof SqlOpQuery) { SqlOpQuery tmp = (SqlOpQuery)op; String queryStr = tmp.getQueryString(); // Cut off trailing ';' queryStr = queryStr.replaceAll("(;|\\s)+$", ""); writer.println("[[" + queryStr + "]]"); } else { writer.println("" + op); } writer.decIndent(); } } @Override public String toString() { ByteArrayOutputStream out = new ByteArrayOutputStream(); IndentedWriter writer = new IndentedWriter(out); write(writer); writer.flush(); writer.close(); String result = out.toString(); return result; } @Override public VarDefinition getVarDefinition() { return mapping.getVarDefinition(); } // HACK: We only use the name for equality testing - take care!!! @Override public int hashCode() { throw new RuntimeException("Don't compare views with equal - use their name instead"); /* final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result;*/ } @Override public boolean equals(Object obj) { throw new RuntimeException("Don't compare views with equal - use their name instead"); /* if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ViewDefinition other = (ViewDefinition) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; */ } }