/** * 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.List ; import org.apache.jena.sparql.syntax.* ; /** Unwrap groups of one where they do not matter. * <p> * They do matter for <code>OPTIONAL { { ?s ?p ?o FILTER(?foo) } }</code>. */ public class ElementTransformCleanGroupsOfOne extends ElementTransformCopyBase { // Improvements: scan group elements to work out for non-reduceable adjacents. // These ones may clash with an adjeact one in the group above. // ElementTransformCleanGroupsOfOne -> ElementTransformCleanGroups public ElementTransformCleanGroupsOfOne() {} @Override public Element transform(ElementGroup eltGroup, List<Element> elts) { if ( elts.size() != 1 ) return super.transform(eltGroup, elts) ; Element elt = elts.get(0) ; if ( ( elt instanceof ElementTriplesBlock ) || ( elt instanceof ElementPathBlock ) || ( elt instanceof ElementFilter ) ) return super.transform(eltGroup, elts) ; // No transformation. return elt ; } // The ElementGroup transformation can be too strong. // Ensure unions have ElementGroup (for compatibility with parsing; // it is safe to have non-ElementGroup in a ElementUnion but it is // a different query syntax tree). @Override public Element transform(ElementUnion eltUnion, List<Element> elts) { ElementUnion el2 = new ElementUnion() ; for ( int i = 0 ; i < elts.size() ; i++ ) { Element el = elts.get(i) ; if ( ! ( el instanceof ElementGroup ) ) { ElementGroup elg = new ElementGroup() ; elg.addElement(el); el = elg ; } el2.addElement(el); } return el2 ; } // Special case: If Optional, and the original had a {{}} protected filter, keep {{}} // transform/ElementGroup has already run so undo if necessary. @Override public Element transform(ElementOptional eltOptional, Element transformedElt) { // RHS of optional is always an ElementGroup in a normal syntax tree. if ( ! ( transformedElt instanceof ElementGroup ) ) { // DRY ElementGroup protectedElt = new ElementGroup() ; protectedElt.addElement(transformedElt); transformedElt = protectedElt ; } // Step 1 : does the original eltOptional has a {{}} RHS? Element x = eltOptional.getOptionalElement() ; if ( ! ( x instanceof ElementGroup ) ) // No. But it is not possible in written query syntax to have a nongroup as the RHS. return super.transform(eltOptional, transformedElt) ; // So far - {}-RHS. ElementGroup eGroup = (ElementGroup)x ; // Is it {{}}? //ElementGroup inner = getGroupInGroup(x) ; if ( eGroup.size() != 1 ) return super.transform(eltOptional, transformedElt) ; Element inner = eGroup.get(0) ; if ( ! ( inner instanceof ElementGroup ) ) return super.transform(eltOptional, transformedElt) ; // Yes - {{}} ElementGroup innerGroup = (ElementGroup)inner ; // Unbundle multiple levels. innerGroup = unwrap(innerGroup) ; boolean mustProtect = containsFilter(innerGroup) ; if ( mustProtect ) { // No need to check for {{}} in elt1 as the transform(ElementGroup) will have processed it. ElementGroup protectedElt = new ElementGroup() ; protectedElt.addElement(transformedElt); return new ElementOptional(protectedElt) ; } // No need to protect - process as usual. return super.transform(eltOptional, transformedElt) ; } private boolean containsFilter(ElementGroup eltGroup) { return eltGroup.getElements().stream().anyMatch(el2 ->( el2 instanceof ElementFilter ) ) ; } // Removed layers of groups of one. Return inner most group. private ElementGroup unwrap(ElementGroup eltGroup) { if ( eltGroup.size() != 1 ) return eltGroup ; Element el = eltGroup.get(0) ; if ( ! ( el instanceof ElementGroup ) ) return eltGroup ; ElementGroup eltGroup2 = (ElementGroup)el ; return unwrap(eltGroup2) ; } }