Java tutorial
/* * Copyright 2012 SFB 632. * * 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 annis.sqlgen; import annis.exceptions.AnnisQLSemanticsException; import annis.service.objects.FrequencyTable; import annis.model.QueryNode; import annis.ql.parser.QueryData; import annis.service.objects.FrequencyTableEntry; import annis.service.objects.FrequencyTableEntryType; import static annis.sqlgen.AbstractSqlGenerator.TABSTOP; import static annis.sqlgen.TableAccessStrategy.NODE_ANNOTATION_TABLE; import static annis.sqlgen.TableAccessStrategy.NODE_TABLE; import annis.service.objects.FrequencyTableQuery; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.springframework.dao.DataAccessException; /** * * @author Thomas Krause <krauseto@hu-berlin.de> */ public class FrequencySqlGenerator extends AbstractSqlGenerator implements WhereClauseSqlGenerator<QueryData>, SelectClauseSqlGenerator<QueryData>, GroupByClauseSqlGenerator<QueryData>, FromClauseSqlGenerator<QueryData>, SqlGeneratorAndExtractor<QueryData, FrequencyTable> { private SolutionSqlGenerator solutionSqlGenerator; public static final Escaper escaper = Escapers.builder().addEscape('\'', "''").build(); @Override public FrequencyTable extractData(ResultSet rs) throws SQLException, DataAccessException { FrequencyTable result = new FrequencyTable(); ResultSetMetaData meta = rs.getMetaData(); while (rs.next()) { Validate.isTrue(meta.getColumnCount() > 1, "frequency table extractor needs at least 2 columns"); Validate.isTrue("count".equalsIgnoreCase(meta.getColumnName(meta.getColumnCount())), "last column name must be \"count\""); long count = rs.getLong("count"); String[] tupel = new String[meta.getColumnCount() - 1]; for (int i = 1; i <= tupel.length; i++) { String colVal = rs.getString(i); if (colVal == null) { tupel[i - 1] = ""; } else { String[] splitted = colVal.split(":", 3); if (splitted.length > 0) { colVal = splitted[splitted.length - 1]; } tupel[i - 1] = colVal; } } // end for each column (except last "count" column) result.addEntry(new FrequencyTable.Entry(tupel, count)); } // end for complete result return result; } @Override public String groupByAttributes(QueryData queryData, List<QueryNode> alternative) { FrequencyTableQuery ext; List<FrequencyTableQuery> freqQueryData = queryData.getExtensions(FrequencyTableQuery.class); Validate.notNull(freqQueryData); Validate.notEmpty(freqQueryData); ext = freqQueryData.get(0); StringBuilder sb = new StringBuilder(); for (int i = 1; i <= ext.size(); i++) { sb.append("value").append(i); if (i < ext.size()) { sb.append(", "); } } return sb.toString(); } @Override public String fromClause(QueryData queryData, List<QueryNode> alternative, String indent) { FrequencyTableQuery ext; List<FrequencyTableQuery> freqQueryData = queryData.getExtensions(FrequencyTableQuery.class); Validate.notNull(freqQueryData); Validate.notEmpty(freqQueryData); ext = freqQueryData.get(0); Multimap<FrequencyTableEntry, String> conditions = conditionsForEntries(ext, queryData, alternative); StringBuilder sb = new StringBuilder(); sb.append(indent).append("(\n"); sb.append(indent); sb.append(getSolutionSqlGenerator().toSql(queryData, indent + TABSTOP)); sb.append(indent).append(") AS solutions\n"); int i = 1; Iterator<FrequencyTableEntry> itEntry = ext.iterator(); while (itEntry.hasNext()) { FrequencyTableEntry e = itEntry.next(); sb.append(indent).append(TABSTOP); sb.append("LEFT JOIN "); String tableSql; if (e.getType() == FrequencyTableEntryType.meta) { tableSql = "corpus_annotation"; } else { tableSql = SelectedFactsFromClauseGenerator.selectedFactsSQL(queryData.getCorpusList(), indent); } sb.append(tableSql); sb.append(" AS v").append(i); sb.append(" ON ("); sb.append(Joiner.on(" AND ").join(conditions.get(e))); sb.append(")\n"); i++; } return sb.toString(); } private Multimap<FrequencyTableEntry, String> conditionsForEntries(List<FrequencyTableEntry> frequencyEntries, QueryData queryData, List<QueryNode> alternative) { Multimap<FrequencyTableEntry, String> conditions = LinkedHashMultimap.create(); int i = 1; ImmutableMap<String, QueryNode> idxNodeVariables = Maps.uniqueIndex(alternative.iterator(), new Function<QueryNode, String>() { @Override public String apply(QueryNode input) { return input.getVariable(); } }); for (FrequencyTableEntry e : frequencyEntries) { if (e.getType() == FrequencyTableEntryType.meta) { List<String> qName = Splitter.on(':').limit(2).omitEmptyStrings().splitToList(e.getKey()); if (qName.size() == 2) { conditions.put(e, "v" + i + ".namespace = '" + escaper.escape(qName.get(0)) + "'"); conditions.put(e, "v" + i + ".name = '" + escaper.escape(qName.get(1)) + "'"); } else { conditions.put(e, "v" + i + ".name = '" + escaper.escape(qName.get(0)) + "'"); } conditions.put(e, "v" + i + ".corpus_ref = solutions.corpus_ref"); } else { // general partition restriction conditions.put(e, "v" + i + ".toplevel_corpus IN (" + StringUtils.join(queryData.getCorpusList(), ",") + ")"); // specificly join on top level corpus conditions.put(e, "v" + i + ".toplevel_corpus = solutions.toplevel_corpus"); // join on node ID QueryNode referencedNode = idxNodeVariables.get(e.getReferencedNode()); if (referencedNode == null) { throw new AnnisQLSemanticsException("No such node \"" + e.getReferencedNode() + "\". " + "Your query contains " + alternative.size() + " node(s), make sure no node definition numbers are greater than this number"); } conditions.put(e, "v" + i + ".id = solutions.id" + referencedNode.getId()); if (e.getType() == FrequencyTableEntryType.span) { conditions.put(e, "v" + i + ".n_sample IS TRUE"); } else if (e.getType() == FrequencyTableEntryType.annotation) { // TODO: support namespaces // filter by selected key conditions.put(e, "v" + i + ".node_annotext LIKE '" + AnnotationConditionProvider.likeEscaper.escape(e.getKey()) + ":%'"); conditions.put(e, "v" + i + ".n_na_sample IS TRUE"); } } i++; } return conditions; } @Override public String selectClause(QueryData queryData, List<QueryNode> alternative, String indent) { TableAccessStrategy tas = tables(null); FrequencyTableQuery ext; List<FrequencyTableQuery> freqQueryData = queryData.getExtensions(FrequencyTableQuery.class); Validate.notNull(freqQueryData); Validate.notEmpty(freqQueryData); ext = freqQueryData.get(0); StringBuilder sb = new StringBuilder(); int i = 1; for (FrequencyTableEntry e : ext) { if (e.getType() == FrequencyTableEntryType.annotation) { sb.append("v").append(i).append(".").append(tas.columnName(NODE_ANNOTATION_TABLE, "qannotext")); } else if (e.getType() == FrequencyTableEntryType.span) { sb.append("('annis:tok:' || ").append("v").append(i).append(".") .append(tas.columnName(NODE_TABLE, "span)")); } else if (e.getType() == FrequencyTableEntryType.meta) { sb.append("('annis_meta:'").append(" || COALESCE(v").append(i).append(".").append("namespace") .append(", '')").append(" || v").append(i).append(".").append("\"name\"").append(" || ':'") .append(" || v").append(i).append(".").append("\"value\")"); } sb.append(" AS value").append(i).append(", "); i++; } sb.append("count(*) AS \"count\""); return sb.toString(); } @Override public Set<String> whereConditions(QueryData queryData, List<QueryNode> alternative, String indent) { // Set<String> conditions = new LinkedHashSet<>(); // // FrequencyTableQuery ext; // List<FrequencyTableQuery> freqQueryData = queryData.getExtensions(FrequencyTableQuery.class); // Validate.notNull(freqQueryData); // Validate.notEmpty(freqQueryData); // ext = freqQueryData.get(0); // // Set<String> conditions = conditionsForEntries(ext, queryData, alternative); return new HashSet<>(); } public SolutionSqlGenerator getSolutionSqlGenerator() { return solutionSqlGenerator; } public void setSolutionSqlGenerator(SolutionSqlGenerator solutionSqlGenerator) { this.solutionSqlGenerator = solutionSqlGenerator; } }