| /* |
| * 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.engine; |
| |
| import java.util.Collection ; |
| import java.util.HashMap ; |
| import java.util.Map ; |
| import java.util.Set ; |
| import org.apache.jena.graph.Node ; |
| import org.apache.jena.sparql.ARQConstants ; |
| import org.apache.jena.sparql.algebra.Op ; |
| 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.graph.NodeTransform; |
| import org.apache.jena.sparql.graph.NodeTransformLib ; |
| |
| /** Support for renaming all the variables in an algebra expression |
| * except for a fixed set to leave alone */ |
| public class Rename |
| { |
| /** Given an old name, and some names already in use, pick a fresh, new name*/ |
| public static Var chooseVarName(Var var, Collection<Var> inUse, String prefix) { |
| Var var2 = var ; |
| do { |
| var2 = Var.alloc(prefix+var2.getName()) ; |
| } while (inUse.contains(var2)) ; |
| return var2 ; |
| } |
| |
| /** Rename one node to another */ |
| public static Op renameNode(Op op, Node oldName, Node newName) { |
| NodeTransform renamer = new RenameNode(oldName, newName) ; |
| return NodeTransformLib.transform(renamer, op) ; |
| } |
| |
| /** Rename one variable to another */ |
| public static Op renameVar(Op op, Var oldName, Var newName) { |
| NodeTransform renamer = new RenameNode(oldName, newName); |
| return NodeTransformLib.transform(renamer, op); |
| } |
| |
| |
| // See also OpVar, VarFinder and VarLib - needs to be pulled together really. |
| // Also need renaming support for renames where only a |
| // certain set are mapped (for (assign (?x ?.0))) |
| |
| private static final String prefix = ARQConstants.allocVarScopeHiding ; |
| |
| /** Rename all variables in a pattern, EXCEPT for those named as constant */ |
| public static Op renameVars(Op op, Collection<Var> constants) { |
| return NodeTransformLib.transform(new RenameAnyVars(constants), op) ; |
| } |
| |
| /** Rename all variables in an expression, EXCEPT for those named as constant */ |
| public static ExprList renameVars(ExprList exprList, Set<Var> constants) { |
| NodeTransform renamer = new RenameAnyVars(constants) ; |
| return NodeTransformLib.transform(renamer, exprList) ; |
| } |
| |
| public static Expr renameVars(Expr expr, Set<Var> constants) { |
| NodeTransform renamer = new RenameAnyVars(constants) ; |
| return NodeTransformLib.transform(renamer, expr) ; |
| } |
| |
| /** Undo the effect of the rename operation, once or repeatedly. |
| * This assumes the op was renamed by VarRename.rename */ |
| public static Op reverseVarRename(Op op, boolean repeatedly) { |
| NodeTransform renamer = new UnrenameAnyVars(prefix, repeatedly) ; |
| return NodeTransformLib.transform(renamer, op) ; |
| } |
| |
| // ---- Transforms that do the renaming and unrenaming. |
| |
| static class RenameNode implements NodeTransform |
| { |
| private final Node oldName ; |
| private final Node newName ; |
| |
| public RenameNode(Node oldName, Node newName) { |
| this.oldName = oldName; |
| this.newName = newName; |
| } |
| |
| @Override |
| public Node apply(Node node) { |
| if ( node.equals(oldName) ) |
| return newName; |
| return node; |
| } |
| } |
| |
| static class RenameAnyVars implements NodeTransform |
| { |
| private final Map<Var, Var> aliases = new HashMap<>() ; |
| private final Collection<Var> constants ; |
| |
| public RenameAnyVars(Collection<Var> constants) { |
| this.constants = constants; |
| } |
| |
| @Override |
| public final Node apply(Node node) { |
| if ( ! Var.isVar(node) ) return node ; |
| if ( constants.contains(node) ) return node ; |
| |
| Var var = (Var)node ; |
| Var var2 = aliases.get(var) ; |
| if ( var2 != null ) |
| return var2 ; |
| // The new name is the old name with a "/" - clashes? |
| // Provided the old name isn't a constant as well, this is safe |
| // if renaming is bottom up and done once. |
| // Really safe - use the global allocator. |
| //var2 = allocator.allocVar() ; |
| |
| var2 = Rename.chooseVarName(var, constants, prefix) ; |
| aliases.put(var, var2) ; |
| return var2 ; |
| } |
| } |
| |
| /** Reverse a renaming (assuming renaming was done by prefixing variable names) */ |
| static class UnrenameAnyVars implements NodeTransform |
| { |
| private final String varPrefix ; |
| private final boolean repeatedly ; |
| |
| public UnrenameAnyVars(String varPrefix, boolean repeatedly) { |
| this.varPrefix = varPrefix; |
| this.repeatedly = repeatedly; |
| } |
| |
| @Override |
| public Node apply(Node node) { |
| if ( !Var.isVar(node) ) |
| return node ; |
| Var var = (Var)node ; |
| String varName = var.getName() ; |
| |
| if ( repeatedly ) { |
| while (varName.startsWith(varPrefix)) |
| varName = varName.substring(varPrefix.length()); |
| } else { |
| if ( varName.startsWith(varPrefix) ) |
| varName = varName.substring(varPrefix.length()); |
| } |
| |
| if ( varName == var.getName() ) |
| return node ; |
| return Var.alloc(varName) ; |
| } |
| } |
| } |