/*
* 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.sparql.syntax.syntaxtransform ;
import java.util.ArrayDeque ;
import java.util.Deque ;
import java.util.List ;
import org.apache.jena.atlas.logging.Log ;
import org.apache.jena.graph.Node ;
import org.apache.jena.query.Query ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.expr.Expr ;
import org.apache.jena.sparql.expr.ExprList ;
import org.apache.jena.sparql.expr.ExprTransform ;
import org.apache.jena.sparql.expr.ExprTransformer ;
import org.apache.jena.sparql.syntax.* ;
/** A bottom-up application of a transformation of SPARQL syntax Elements.
* {@linkplain QueryTransformOps#transform} provides the mechanism
* to apply to a {@linkplain Query}.
* @see ElementTransformCopyBase
* @see UpdateTransformOps#transform
*/
public class ElementTransformer {
private static ElementTransformer singleton = new ElementTransformer() ;
/** Get the current transformer */
public static ElementTransformer get() {
return singleton ;
}
/** Set the current transformer - use with care */
public static void set(ElementTransformer value) {
ElementTransformer.singleton = value ;
}
/** Transform an algebra expression */
public static Element transform(Element element, ElementTransform transform) {
return transform(element, transform, null, null, null) ;
}
/** Transformation with specific ElementTransform and ExprTransform */
public static Element transform(Element element, ElementTransform transform, ExprTransform exprTransform) {
return get().transformation(element, transform, exprTransform, null, null) ;
}
public static Element transform(Element element, ElementTransform transform, ExprTransform exprTransform,
ElementVisitor beforeVisitor, ElementVisitor afterVisitor) {
return get().transformation(element, transform, exprTransform, beforeVisitor, afterVisitor) ;
}
// To allow subclassing this class, we use a singleton pattern
// and these protected methods.
protected Element transformation(Element element, ElementTransform transform, ExprTransform exprTransform,
ElementVisitor beforeVisitor, ElementVisitor afterVisitor) {
ApplyTransformVisitor v = new ApplyTransformVisitor(transform, exprTransform) ;
return transformation(v, element, beforeVisitor, afterVisitor) ;
}
protected Element transformation(ApplyTransformVisitor transformApply, Element element,
ElementVisitor beforeVisitor, ElementVisitor afterVisitor) {
if ( element == null ) {
Log.warn(this, "Attempt to transform a null element - ignored") ;
return element ;
}
return applyTransformation(transformApply, element, beforeVisitor, afterVisitor) ;
}
/** The primitive operation to apply a transformation to an Op */
protected Element applyTransformation(ApplyTransformVisitor transformApply, Element element,
ElementVisitor beforeVisitor, ElementVisitor afterVisitor) {
ElementWalker.walk(element, transformApply, beforeVisitor, afterVisitor) ;
Element r = transformApply.result() ;
return r ;
}
protected ElementTransformer() {}
static class ApplyTransformVisitor implements ElementVisitor {
protected final ElementTransform transform ;
private final ExprTransform exprTransform ;
private final Deque<Element> stack = new ArrayDeque<Element>() ;
protected final Element pop() {
return stack.pop() ;
}
protected final void push(Element elt) {
stack.push(elt) ;
}
public ApplyTransformVisitor(ElementTransform transform, ExprTransform exprTransform) {
if ( transform == null )
transform = ElementTransformIdentity.get() ;
this.transform = transform ;
this.exprTransform = exprTransform ;
}
final Element result() {
if ( stack.size() != 1 )
Log.warn(this, "Stack is not aligned") ;
return pop() ;
}
@Override
public void visit(ElementTriplesBlock el) {
Element el2 = transform.transform(el) ;
push(el2) ;
}
@Override
public void visit(ElementPathBlock el) {
Element el2 = transform.transform(el) ;
push(el2) ;
}
@Override
public void visit(ElementFilter el) {
Expr expr = el.getExpr() ;
Expr expr2 = transformExpr(expr, exprTransform) ;
Element el2 = transform.transform(el, expr2) ;
push(el2) ;
}
@Override
public void visit(ElementAssign el) {
Var v = el.getVar() ;
Var v1 = TransformElementLib.applyVar(v, exprTransform) ;
Expr expr = el.getExpr() ;
Expr expr1 = ExprTransformer.transform(exprTransform, expr) ;
Element el2 = transform.transform(el, v1, expr1 ) ;
push(el2) ;
}
@Override
public void visit(ElementBind el) {
Var v = el.getVar() ;
Var v1 = TransformElementLib.applyVar(v, exprTransform) ;
Expr expr = el.getExpr() ;
Expr expr1 = ExprTransformer.transform(exprTransform, expr) ;
Element el2 = transform.transform(el, v1, expr1 ) ;
push(el2) ;
}
@Override
public void visit(ElementData el) {
transform.transform(el) ;
push(el) ;
}
@Override
public void visit(ElementOptional el) {
Element elSub = pop() ;
Element el2 = transform.transform(el, elSub) ;
push(el2) ;
}
@Override
public void visit(ElementGroup el) {
ElementGroup newElt = new ElementGroup() ;
boolean b = transformFromTo(el.getElements(), newElt.getElements()) ;
Element el2 = transform.transform(el, newElt.getElements()) ;
push(el2) ;
}
@Override
public void visit(ElementUnion el) {
ElementUnion newElt = new ElementUnion() ;
boolean b = transformFromTo(el.getElements(), newElt.getElements()) ;
Element el2 = transform.transform(el, newElt.getElements()) ;
push(el2) ;
}
private boolean transformFromTo(List<Element> elts, List<Element> elts2) {
boolean changed = false ;
for (Element elt : elts) {
Element elt2 = pop() ;
changed = (changed || (elt != elt2)) ;
// Add reversed.
elts2.add(0, elt2) ;
}
return changed ;
}
@Override
public void visit(ElementDataset el) {
Element sub = pop() ;
Element el2 = transform.transform(el, sub) ;
push(el2) ;
}
@Override
public void visit(ElementNamedGraph el) {
Node n = el.getGraphNameNode() ;
Node n1 = transformNode(n) ;
Element elt1 = pop() ;
Element el2 = transform.transform(el, n1, elt1) ;
push(el2) ;
}
@Override
public void visit(ElementExists el) {
Element elt = el.getElement() ;
Element elt1 = subElement(elt) ;
Element el2 = transform.transform(el, elt1) ;
push(el2) ;
}
@Override
public void visit(ElementNotExists el) {
Element elt = el.getElement() ;
Element elt1 = subElement(elt) ;
Element el2 = transform.transform(el, elt1) ;
push(el2) ;
}
// When you need to force the walking of the tree ...
// EXISTS / NOT EXISTS
private Element subElement(Element elt) {
ElementWalker.walk(elt, this) ;
Element elt1 = pop() ;
return elt1 ;
}
@Override
public void visit(ElementMinus el) {
Element elt = el.getMinusElement() ;
Element elt1 = pop() ;
if ( elt == elt1 )
push(el) ;
else
push(new ElementMinus(elt1)) ;
}
@Override
public void visit(ElementService el) {
Node n = el.getServiceNode() ;
Node n1 = transformNode(n) ;
Element elt1 = pop() ;
Element el2 = transform.transform(el, n1, elt1) ;
push(el2) ;
}
@Override
public void visit(ElementSubQuery el) {
Query newQuery = QueryTransformOps.transform(el.getQuery(), transform, exprTransform) ;
push(new ElementSubQuery(newQuery)) ;
}
private Node transformNode(Node n) {
if ( exprTransform == null )
return n ;
return TransformElementLib.apply(n, exprTransform) ;
}
private ExprList transformExpr(ExprList exprList, ExprTransform exprTransform) {
if ( exprList == null || exprTransform == null )
return exprList ;
return ExprTransformer.transform(exprTransform, exprList) ;
}
private Expr transformExpr(Expr expr, ExprTransform exprTransform) {
if ( expr == null || exprTransform == null )
return expr ;
return ExprTransformer.transform(exprTransform, expr) ;
}
}
}