/* * 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.sse.lang; import java.util.ArrayDeque ; import java.util.Deque ; import org.apache.jena.sparql.sse.Item ; import org.apache.jena.sparql.sse.ItemList ; /** Resolve syntacic forms like (base ...) and (prefix...) * where the syntax modifies the enclosed sub term. * * * Forms: * (FORM DECL... TERM) => where TERM is the result. * Examples * (prefix (PREFIXES) TERM) => TERM with prefix names expanded * (base IRI TERM) => TERM with IRIs resolved to absolute IRIs * * The DECL part can not itself have nested, independent forms * unless a subclass (carefully) manages that. */ public abstract class ParseHandlerForm extends ParseHandlerPlain { // generally: (FORM DECL* TERM?) // TERM may be absent, in which case the FORM makes // no contribution to the output (it just disappears). // Stackable to enable multiple forms?? // Or "FormHandler" with a dispatch on registered tags? private boolean inDecl = false ; private FrameStack frameStack = new FrameStack() ; public ParseHandlerForm() {} @Override public void listStart(int line, int column) { super.listStart(line, column) ; } @Override public void listFinish(int line, int column) { ItemList list = currentList() ; if ( ! frameStack.isCurrent(list) ) { // Nothing special - proceed as normal. super.listFinish(line, column) ; return ; } if ( inDecl ) throwException("Inconsistent form: Still in DECL at end of the form", line, column) ; // For later: no exception: ensure this is cleared. inDecl = false ; finishForm(list) ; // Frame Frame f = frameStack.pop() ; // Drop the form list. popList() ; //setCurrentItem(null) ; // Clear, in case top level item is a form of nothing. // Form output skipped if no result registered. Item item = f.result ; // If all forms at least evaluate to nil. // if ( item == null ) // item = Item.createNil(list.getLine(), list.getColumn()) ; // And emit a result as a listAdd. // Must go through our listAdd() here to handle nested forms. // item==null : remove nil code above to allow forms that have no output. // if ( item != null ) listAdd(item) ; } @Override protected void listAdd(Item item) { // Always add to the current list, even for (base...) and (prefix...) // Then change the result list later. super.listAdd(item) ; ItemList list = super.currentList() ; if ( list == null ) // Top level is outside a list. Can't be a form. return ; Frame lastFrame = frameStack.getCurrent() ; if ( ! inDecl && /*! sameAsLast &&*/ list.size() == 1 && isForm(list.getFirst() ) ) { startForm(list) ; Frame f = new Frame(list) ; frameStack.push(f) ; inDecl = true ; return ; } if ( inDecl ) { // Only trigger form operations when items at the top of the form are seen. boolean atTopOfDecl = ( lastFrame != null && lastFrame.listItem == list ) ; if ( ! atTopOfDecl ) return ; declItem(list, item) ; if ( endOfDecl(list, item) ) { inDecl = false ; // Already added. return ; } } } protected boolean inFormDecl() { return inDecl; } abstract protected void declItem(ItemList list, Item item) ; abstract protected boolean isForm(Item tag) ; abstract protected boolean endOfDecl(ItemList list, Item item) ; abstract protected void startForm(ItemList list) ; abstract protected void finishForm(ItemList list) ; protected void setFormResult(Item item) { if ( frameStack.getCurrent() == null ) throwException("Internal error : no current form", item.getLine(), item.getColumn()) ; frameStack.getCurrent().result = item ; } private static class Frame { ItemList listItem ; Item result ; Frame(ItemList listItem) { this.listItem = listItem ; } } // ---------------- private static class FrameStack { private Deque<Frame> frames = new ArrayDeque<>() ; boolean isCurrent(ItemList list) { if ( frames.size() == 0 ) return false ; Frame f = frames.peek(); return f.listItem == list ; } Frame getCurrent() { if ( frames.size() == 0 ) return null ; return frames.peek() ; } void push(Frame f) { frames.push(f) ; } Frame pop() { return frames.pop() ; } } }