/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed 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 net.ontopia.topicmaps.query.parser; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Set; import net.ontopia.utils.CompactHashSet; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.infoset.impl.basic.URILocator; import net.ontopia.topicmaps.core.TMObjectIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.query.core.DeclarationContextIF; import net.ontopia.topicmaps.query.core.BadObjectReferenceException; import net.ontopia.topicmaps.query.core.InvalidQueryException; import net.ontopia.topicmaps.query.parser.TologOptions; /** * INTERNAL: Represents the local context in which a tolog query or * rule file is being parsed. Context may be the context bound to a * QueryProcessorIF object, or it may be only a transient local * context for a query. */ public class LocalParseContext implements ParseContextIF, DeclarationContextIF { private ParseContextIF subcontext; private Map<String, PrefixBinding> bindings; private Map predicates; Set loading_modules = new CompactHashSet(); public LocalParseContext(ParseContextIF subcontext) { this.subcontext = subcontext; this.bindings = new HashMap<String, PrefixBinding>(); this.predicates = new HashMap(); } public TopicMapIF getTopicMap() { return subcontext.getTopicMap(); } public LocatorIF resolveQName(QName qname) { PrefixBinding binding = bindings.get(qname.getPrefix()); if (binding == null) throw new OntopiaRuntimeException("No such prefix " + qname.getPrefix()); if (binding.getQualification() != SUBJECT_IDENTIFIER) throw new OntopiaRuntimeException("Prefix " + qname.getPrefix() + " is not a subject identifier prefix"); try { return new URILocator(binding.getUri(qname.getLocalName())); } catch (MalformedURLException e) { throw new OntopiaRuntimeException(e); } } public void addPrefixBinding(String prefix, String uri, int qualification) throws AntlrWrapException { if (bindings.containsKey(prefix)) throw new AntlrWrapException(new InvalidQueryException("Prefix " + prefix + " already bound")); if (qualification == MODULE) { ModuleIF module = getModule(uri); if (module == null) { LocalParseContext modulectx = new LocalParseContext(subcontext); TologParser parser = new TologParser(modulectx, TologOptions.defaults); try { if (isLoading(uri)) throw new InvalidQueryException("Importing an already imported module is not allowed: '" + uri + "'"); // attempt to load module from class loader / classpath ClassLoader cl = Thread.currentThread().getContextClassLoader(); InputStream istream = cl.getResourceAsStream(uri); try { // NOTE: this is a hack to prevent infinite recursion when // loading modules. modulectx.loading_modules.add(uri); // resolve uri absolute against topic map base address if (istream == null) istream = new URL(absolutify(uri).getAddress()).openStream(); parser.load(new InputStreamReader(istream)); } finally { modulectx.loading_modules.remove(uri); if (istream != null) istream.close(); } } catch (IOException e) { throw new AntlrWrapException(e); } catch (InvalidQueryException e) { throw new AntlrWrapException(e); } module = modulectx.getModule(); } bindings.put(prefix, new PrefixBinding(uri, module)); } else { absolutify(uri); // done to get error messages here bindings.put(prefix, new PrefixBinding(uri, qualification)); } } public boolean isLoading(String uri) { return (subcontext.isLoading(uri) || loading_modules.contains(uri)); } public boolean isBuiltInPredicate(String name) { return subcontext.isBuiltInPredicate(name); } public void addPredicate(PredicateIF predicate) throws AntlrWrapException { if (predicates.containsKey(predicate.getName())) throw new AntlrWrapException(new InvalidQueryException("Predicate " + predicate.getName() + " exists already")); if (subcontext.isBuiltInPredicate(predicate.getName())) throw new AntlrWrapException(new InvalidQueryException( "Predicate " + predicate.getName() + " is a built-in predicate, and cannot "+ "be redefined.")); predicates.put(predicate.getName(), predicate); } public TopicIF getTopic(QName qname) throws AntlrWrapException { TMObjectIF object = getObject(qname); if (!(object instanceof TopicIF)) throw new AntlrWrapException( new InvalidQueryException("Found " + qname + ", referring to non-topic " + object)); return (TopicIF) object; } public TMObjectIF getObject(QName qname) throws AntlrWrapException { return checkReference(getObject_(qname), qname.toString()); } private TMObjectIF getObject_(QName qname) throws AntlrWrapException { String prefix = qname.getPrefix(); String localname = qname.getLocalName(); if (prefix == null) return subcontext.getObject(qname); PrefixBinding binding = bindings.get(prefix); if (binding == null) return subcontext.getObject(qname); switch (binding.getQualification()) { case SUBJECT_LOCATOR: return getTopicBySubjectLocator(binding.getUri(localname)); case SUBJECT_IDENTIFIER: return getTopicBySubjectIdentifier(binding.getUri(localname)); case ITEM_IDENTIFIER: return getObjectByItemId(binding.getUri(localname)); default: throw new AntlrWrapException( new InvalidQueryException("Prefix " + prefix + " bound to a module")); } } public PredicateIF getPredicate(QName qname, boolean assoc) throws AntlrWrapException { if (qname.getPrefix() == null) { PredicateIF predicate = (PredicateIF) predicates.get(qname.getLocalName()); if (predicate == null) predicate = subcontext.getPredicate(qname, assoc); return predicate; } PrefixBinding binding = bindings.get(qname.getPrefix()); if (binding == null) return subcontext.getPredicate(qname, assoc); if (binding.getQualification() == MODULE) return binding.getModule().getPredicate(qname.getLocalName()); else return getPredicate(getTopic(qname), assoc); } public PredicateIF getPredicate(TopicIF topic, boolean assoc) { return subcontext.getPredicate(topic, assoc); } public PredicateIF getPredicate(ParsedRule rule) { return subcontext.getPredicate(rule); } public ModuleIF getModule(String uri) { return subcontext.getModule(uri); } public LocatorIF absolutify(String uriref) throws AntlrWrapException { return subcontext.absolutify(uriref); } public TMObjectIF getObjectByObjectId(String id) throws AntlrWrapException { return checkReference(subcontext.getObjectByObjectId(id), "@" + id); } public TopicIF getTopicBySubjectIdentifier(String uri) throws AntlrWrapException { return subcontext.getTopicBySubjectIdentifier(uri); } public TopicIF getTopicBySubjectLocator(String uri) throws AntlrWrapException { return subcontext.getTopicBySubjectLocator(uri); } public TMObjectIF getObjectByItemId(String uri) throws AntlrWrapException { return checkReference(subcontext.getObjectByItemId(uri), uri); } // --- Methods specific to LocalParseContext /** * INTERNAL: Creates a module consisting of all the rules loaded * into the local context. */ private ModuleIF getModule() { return new RuleFileModule(predicates); } // --- Internal methods private TMObjectIF checkReference(TMObjectIF value, String token) throws AntlrWrapException { if (value == null) throw new AntlrWrapException(new BadObjectReferenceException("No object for " + token)); return value; } public void dump() { System.out.println("===== LocalParseContext " + this); for (String prefix : bindings.keySet()) { PrefixBinding bind = bindings.get(prefix); System.out.println(prefix + " : " + bind.uri); } subcontext.dump(); } // --- Prefix binding static class PrefixBinding { private String uri; private int qualification; private ModuleIF module; public PrefixBinding(String uri, int qualification) { this.uri = uri; this.qualification = qualification; } public PrefixBinding(String uri, ModuleIF module) { this.uri = uri; this.qualification = MODULE; this.module = module; } public String getUri(String localpart) { return uri + localpart; } public int getQualification() { return qualification; } public ModuleIF getModule() { return module; } } // --- Rule file module static class RuleFileModule implements ModuleIF { private Map predicates; public RuleFileModule(Map predicates) { this.predicates = predicates; } public PredicateIF getPredicate(String name) { return (PredicateIF) predicates.get(name); } } }