/* * #! * 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. * !# */ // $Id$ package net.ontopia.topicmaps.query.parser; import java.util.Map; import java.util.Set; import java.util.List; import java.util.HashMap; import java.util.ArrayList; import java.util.Collections; import java.io.Reader; import java.io.StringReader; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.topicmaps.core.DataTypes; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.core.TopicMapBuilderIF; import net.ontopia.topicmaps.utils.ctm.CTMLexer; import net.ontopia.topicmaps.utils.ctm.CTMParser; import net.ontopia.topicmaps.utils.ctm.Template; import net.ontopia.topicmaps.utils.ctm.BuilderEventHandler; import net.ontopia.topicmaps.utils.ctm.TemplateEventHandler; import net.ontopia.topicmaps.utils.ctm.ValueGeneratorIF; import net.ontopia.topicmaps.utils.ctm.ValueGenerator; import net.ontopia.topicmaps.query.core.InvalidQueryException; import net.ontopia.topicmaps.query.impl.basic.QueryMatches; import net.ontopia.topicmaps.xml.InvalidTopicMapException; import antlr.RecognitionException; import antlr.TokenStreamRecognitionException; import antlr.TokenStreamException; import antlr.TokenStreamIOException; /** * INTERNAL: Represents a parsed INSERT statement. */ public class InsertStatement extends ModificationStatement { private Template template; private CTMParseContext context; private List<String> parameters; // the order of the parameters to the CTM part public InsertStatement() { super(); } public int doStaticUpdates(TopicMapIF topicmap, Map arguments) throws InvalidQueryException { BuilderEventHandler handler = new BuilderEventHandler(topicmap.getBuilder(), context); template.invoke(Collections.EMPTY_LIST, handler); return 1; } public int doUpdates(QueryMatches matches) throws InvalidQueryException { TopicMapIF topicmap = matches.getQueryContext().getTopicMap(); BuilderEventHandler handler = new BuilderEventHandler(topicmap.getBuilder(), context); // the argument values to be passed to the template List arguments = new ArrayList(matches.colcount); for (int ix = 0; ix < matches.colcount; ix++) arguments.add(null); // making a slot for value filled in later // a mapping from the column indexes in matches to the corresponding // parameter indexes in the arguments list int[] colix = new int[parameters.size()]; for (int ix = 0; ix < parameters.size(); ix++) { String name = parameters.get(ix).substring(1); // remove the $ colix[ix] = matches.getVariableIndex(name); } for (int row = 0; row <= matches.last; row++) { for (int ix = 0; ix < parameters.size(); ix++) arguments.set(ix, makeGenerator(matches.data[row][colix[ix]])); template.invoke(arguments, handler); context.endContext(); } return matches.last + 1; } public String toString() { String str = "insert ..."; // FIXME: stringfy CTM part if (query != null) str += "\nfrom" + query.toStringFromPart(); return str; } public void setCTMPart(String ctm, ParseContextIF context) throws InvalidQueryException { // this sets parameter list to all parameters used in the query, // but these aren't necessarily all used in the INSERT part. still, // this list allows the CTM parser to reject unknown parameters and // still work the way it usually does. we reset the parameter list // once the CTM has been parsed. if (query == null) parameters = Collections.EMPTY_LIST; else { String[] varnames = query.getSelectedVariableNames(); parameters = new ArrayList<String>(varnames.length); for (int ix = 0; ix < varnames.length; ix++) parameters.add("$" + varnames[ix]); } // actually do the CTM parsing try { // see ctm.g tolog_insert comment for why we add an "end" Reader reader = new StringReader(ctm + " end"); CTMLexer lexer = new CTMLexer(reader); CTMParser parser = new CTMParser(lexer); parser.init(); parser.setBase(context.getTopicMap().getStore().getBaseAddress()); parser.setTopicMap(context.getTopicMap(), null); TemplateEventHandler handler = new TemplateEventHandler(null, parameters, null); this.context = new CTMParseContext(context, parser.getContext()); parser.setHandler(handler, this.context); parser.tolog_insert(); template = handler.getTemplate(); } catch (AntlrWrapException ex) { throw new InvalidQueryException("IO exception: " + ex.getException()); } catch (RecognitionException ex) { throw new InvalidQueryException("Lexical error at :" + ex.line + ":" + ex.column + ": "+ ex.getMessage()); } catch (TokenStreamRecognitionException e) { RecognitionException ex = e.recog; throw new InvalidQueryException("Lexical error at:" + ex.line + ":" + ex.column + ": "+ ex.getMessage()); } catch (TokenStreamIOException ex) { throw new InvalidQueryException("IO exception: " + ex.io); } catch (TokenStreamException ex) { throw new InvalidQueryException("Lexical error: " + ex.getMessage()); } catch (InvalidTopicMapException e) { throw new InvalidQueryException("Error in CTM part: " + e.getMessage()); } if (query != null) { // deducing the set of parameters to the virtual template based on // what parameters are actually used in the INSERT part. Set<String> used = template.getUsedParameters(); parameters = new ArrayList<String>(used.size()); for (String param : used) parameters.add(param); template.setParameters(parameters); // finally, adjust the SELECT part of the query to match the parameters // actually used in the INSERT, to project the query down correctly. List<Variable> vars = new ArrayList<Variable>(parameters.size()); for (String name : parameters) vars.add(new Variable(name)); query.setSelectedVariables(vars); } } private static ValueGeneratorIF makeGenerator(Object value) { if (value instanceof TopicIF) return new ValueGenerator((TopicIF) value, null, null, null); else if (value instanceof String) return new ValueGenerator(null, (String) value, DataTypes.TYPE_STRING, null); else throw new OntopiaRuntimeException("Can't make generator for " + value); } // --- CTM parse context wrapping tolog parse context // lifetime for this context is the duration of the update statement static class CTMParseContext implements net.ontopia.topicmaps.utils.ctm.ParseContextIF { private ParseContextIF tologctx; private net.ontopia.topicmaps.utils.ctm.ParseContextIF ctmctx; private Map<String, TopicIF> wildcards; private CTMParseContext(ParseContextIF tologctx, net.ontopia.topicmaps.utils.ctm.ParseContextIF ctmctx) { this.tologctx = tologctx; this.ctmctx = ctmctx; this.wildcards = new HashMap<String, TopicIF>(); } public void addPrefix(String prefix, LocatorIF locator) { throw new UnsupportedOperationException(); } public void addIncludeUri(LocatorIF uri) { throw new UnsupportedOperationException(); } public Set getIncludeUris() { throw new UnsupportedOperationException(); } public LocatorIF resolveQname(String qname) { return tologctx.resolveQName(new QName(qname)); } public ValueGeneratorIF getTopicById(String id) { return ctmctx.getTopicById(id); } public ValueGeneratorIF getTopicByItemIdentifier(LocatorIF itemid) { return ctmctx.getTopicByItemIdentifier(itemid); } public ValueGeneratorIF getTopicBySubjectLocator(LocatorIF subjloc) { return ctmctx.getTopicBySubjectLocator(subjloc); } public ValueGeneratorIF getTopicBySubjectIdentifier(LocatorIF subjid) { return ctmctx.getTopicBySubjectIdentifier(subjid); } public ValueGeneratorIF getTopicByQname(String qname) { return ctmctx.getTopicBySubjectIdentifier(resolveQname(qname)); } public TopicIF makeTopicById(String id) { return ctmctx.makeTopicById(id); } public TopicIF makeTopicByItemIdentifier(LocatorIF itemid) { return ctmctx.makeTopicByItemIdentifier(itemid); } public TopicIF makeTopicBySubjectLocator(LocatorIF subjloc) { return ctmctx.makeTopicBySubjectLocator(subjloc); } public TopicIF makeTopicBySubjectIdentifier(LocatorIF subjid) { return ctmctx.makeTopicBySubjectIdentifier(subjid); } public TopicIF makeAnonymousTopic() { TopicMapIF topicmap = tologctx.getTopicMap(); TopicMapBuilderIF builder = topicmap.getBuilder(); TopicIF topic = builder.makeTopic(); return topic; } public TopicIF makeAnonymousTopic(String wildcard_name) { TopicIF topic = wildcards.get(wildcard_name); if (topic == null) { topic = makeAnonymousTopic(); wildcards.put(wildcard_name, topic); } return topic; } public void registerTemplate(String name, Template template) { throw new UnsupportedOperationException(); } public Template getTemplate(String name , int paramcount) { throw new UnsupportedOperationException(); } public Map getTemplates() { throw new UnsupportedOperationException(); } // finished one row; named wildcards must be released private void endContext() { wildcards.clear(); } } }