/* * 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 java.util.Iterator ; import org.apache.jena.atlas.lib.StrUtils ; import org.apache.jena.shared.PrefixMapping ; import org.apache.jena.sparql.core.Prologue ; import org.apache.jena.sparql.sse.Item ; import org.apache.jena.sparql.sse.ItemList ; import org.apache.jena.sparql.sse.builders.BuilderPrefixMapping ; /** 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 class ParseHandlerResolver extends ParseHandlerForm { private static final String prefixTag = "prefix" ; private static final String baseTag = "base" ; private PrefixMapping topMap = null ; private String topBase = null ; private Prologue prologue = null ; private ItemList declList = null ; private Deque<Prologue> state = new ArrayDeque<>() ; // Previous prologues (not the current one) public ParseHandlerResolver(Prologue p) { prologue = p ; } @Override protected void declItem(ItemList list, Item item) { if ( list != declList ) // Deeper return ; // Prefix - deeper than one. boolean isBase = list.get(0).isSymbol(baseTag) ; boolean isPrefix = list.get(0).isSymbol(prefixTag) ; // Old state has already been saved. if ( isBase ) { if ( ! item.isNode() ) throwException("(base ...): not an RDF node for the base.", item) ; if ( ! item.getNode().isURI() ) throwException("(base ...): not an IRI for the base.", item) ; String baseIRI = item.getNode().getURI() ; prologue = prologue.sub(baseIRI) ; // Remeber first base seen if ( topBase == null ) topBase = baseIRI ; return ; } if ( isPrefix ) { PrefixMapping newMappings = BuilderPrefixMapping.build(item) ; prologue = prologue.sub(newMappings) ; // Remember first prefix mapping seen. if( topMap == null ) topMap = newMappings ; return ; } throwException("Inconsistent: "+list.shortString(), list) ; } @Override protected boolean endOfDecl(ItemList list, Item item) { // Both (base...) and (prefix...) have one decl item if ( declList == list && list.size() == 2 ) { declList = null ; return true ; } return false ; } @Override protected boolean isForm(Item tag) { return tag.isSymbol(baseTag) || tag.isSymbol(prefixTag) ; } @Override protected void startForm(ItemList list) { // Remember the top of declaration declList = list ; state.push(prologue) ; } private void dump() { Iterator<Prologue> iter = state.iterator() ; for ( ; iter.hasNext() ; ) { Prologue p = iter.next() ; System.out.println(" Prologue: "+p.getBaseURI()) ; } } @Override protected void finishForm(ItemList list) { // Check list length prologue = state.pop() ; // Restore state // Choose the result. if ( list.size() > 2 ) { Item item = list.getLast() ; super.setFormResult(item) ; } } @Override public void emitIRI(int line, int column, String iriStr) { iriStr = resolveIRI(iriStr, line, column) ; super.emitIRI(line, column, iriStr) ; } @Override public void emitPName(int line, int column, String pname) { if ( inFormDecl() ) { // Record a faked PName. Works with BuilderPrefixMapping Item item = Item.createSymbol(pname, line, column) ; listAdd(item) ; return ; } String iriStr = resolvePrefixedName(pname, line, column) ; super.emitIRI(line, column, iriStr) ; } @Override protected String resolvePrefixedName(String pname, int line, int column) { if ( prologue.getPrefixMapping() == null ) throwException("No prefix mapping for prefixed name: "+pname, line, column) ; if ( ! StrUtils.contains(pname, ":") ) throwException("Prefixed name does not have a ':': "+pname, line, column) ; String uri = prologue.expandPrefixedName(pname) ; if ( uri == null ) throwException("Can't resolve prefixed name: "+pname, line, column) ; return uri ; } private String resolveIRI(String iriStr, int line, int column) { if ( prologue.getResolver() != null ) return prologue.getResolver().resolveToStringSilent(iriStr) ; return iriStr ; } }