/*
* 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 ro.nextreports.engine.querybuilder.sql.dialect;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Types;
import java.sql.Timestamp;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Represents a dialect of SQL implemented by a particular RDBMS.
* <br>
* Subclasses should provide a public default constructor that <tt>register()</tt>
* a set of type mappings and.<br>
* <br>
*
* @author Decebal Suiu
*/
public abstract class AbstractDialect implements Dialect {
protected static final Log LOG = LogFactory.getLog(AbstractDialect.class);
private List<ColumnTypeMatcher> columnTypeMatchers = new ArrayList<ColumnTypeMatcher>();
// private Map<String, Integer> jdbcTypes = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
private Map<String, Integer> jdbcTypes = new HashMap<String, Integer>();
private Map<Integer, String> javaTypes = new HashMap<Integer, String>();
// uppercase keywords : an array of known keywords that are allowed to be used as
// table or column names
protected String[] keywords = new String[0];
public AbstractDialect() {
LOG.info("Using dialect: " + this);
setKeywords();
registerDefaultJavaTypes();
}
/**
* Get <tt>java.sql.Types</tt> typecode of the column database associated
* with the given sql type, precision and scale.
*
* @param type sql type
* @param precision the precision of the column
* @param scale the scale of the column
*
* @return the column typecode
* @throws DialectException
*/
public final int getJdbcType(String type, int precision, int scale) throws DialectException {
// TODO ?!
// SQLite returns "null" for type if we use rsmd.getColumnTypeName
if ((type == null) || "null".equals(type)) {
return Types.OTHER;
}
List<ColumnTypeMatcher> typeMatchers = matchType(type, columnTypeMatchers);
if (typeMatchers.size() == 0) {
throw new DialectException("Cannot match the type '" + type + "' to a jdbc type");
}
if (typeMatchers.size() == 1) {
return jdbcTypes.get(typeMatchers.get(0).getColumnType());
}
List<ColumnTypeMatcher> precisionMatchers = matchPrecision(precision, typeMatchers);
if (precisionMatchers.size() == 0) {
throw new DialectException("Cannot match the precision '" + precision + "' to a jdbc type");
}
if (precisionMatchers.size() == 1) {
return jdbcTypes.get(precisionMatchers.get(0).getColumnType());
}
List<ColumnTypeMatcher> scaleMatchers = matchScale(scale, typeMatchers);
if (scaleMatchers.size() == 0) {
throw new DialectException("Cannot match the scale '" + scale + "' to a jdbc type");
}
if (scaleMatchers.size() == 1) {
return jdbcTypes.get(scaleMatchers.get(0).getColumnType());
}
return jdbcTypes.get(chooseOne(scaleMatchers).getColumnType());
}
public final String getJavaType(String type, int precision, int scale) throws DialectException {
return javaTypes.get(getJdbcType(type, precision, scale));
}
/**
* Subclasses register a typename for the given type code.
*
* @param columnType the database column type
* @param jdbcType <tt>java.sql.Types</tt> typecode
*/
protected void registerColumnType(String columnType, int jdbcType) {
columnTypeMatchers.add(new ColumnTypeMatcher(columnType));
jdbcTypes.put(columnType, jdbcType);
}
public boolean hasProcedureWithCursor() {
return false;
}
public boolean schemaBeforeCatalog() {
return true;
}
protected void registerDefaultJavaTypes() {
registerJavaType(Types.BIT, Boolean.class.getName());
registerJavaType(Types.BOOLEAN, Boolean.class.getName());
registerJavaType(Types.TINYINT, Byte.class.getName());
registerJavaType(Types.SMALLINT, Short.class.getName());
// registerJavaType(Types.CHAR, Character.class.getName());
registerJavaType(Types.CHAR, String.class.getName());
registerJavaType(Types.VARCHAR, String.class.getName());
registerJavaType(Types.DATE, Date.class.getName());
registerJavaType(Types.TIME, Time.class.getName());
registerJavaType(Types.TIMESTAMP, Timestamp.class.getName());
registerJavaType(Types.DOUBLE, Double.class.getName());
registerJavaType(Types.FLOAT, Float.class.getName());
registerJavaType(Types.INTEGER, Integer.class.getName());
registerJavaType(Types.BIGINT, BigInteger.class.getName());
// registerJavaType(Types.BIGINT, Long.class.getName());
registerJavaType(Types.NUMERIC, BigDecimal.class.getName());
registerJavaType(Types.DECIMAL, BigDecimal.class.getName());
registerJavaType(Types.BINARY, byte[].class.getName());
registerJavaType(Types.VARBINARY, byte[].class.getName());
registerJavaType(Types.BLOB, String.class.getName());
registerJavaType(Types.CLOB, String.class.getName());
registerJavaType(Types.REAL, String.class.getName());
registerJavaType(Types.OTHER, Object.class.getName());
}
protected void registerJavaType(int jdbcType, String javaType) {
javaTypes.put(jdbcType, javaType);
}
protected List<ColumnTypeMatcher> matchType(String type, List<ColumnTypeMatcher> columnTypeMatchers) {
List<ColumnTypeMatcher> typeMatchers = new ArrayList<ColumnTypeMatcher>();
for (ColumnTypeMatcher matcher : columnTypeMatchers) {
if (matcher.matchType(type)) {
typeMatchers.add(matcher);
}
}
return typeMatchers;
}
protected List<ColumnTypeMatcher> matchPrecision(int precision, List<ColumnTypeMatcher> columnTypeMatchers) {
List<ColumnTypeMatcher> precisionMatchers = new ArrayList<ColumnTypeMatcher>();
for (ColumnTypeMatcher matcher : columnTypeMatchers) {
if (matcher.matchPrecision(precision)) {
precisionMatchers.add(matcher);
}
}
return precisionMatchers;
}
protected List<ColumnTypeMatcher> matchScale(int scale, List<ColumnTypeMatcher> columnTypeMatchers) {
List<ColumnTypeMatcher> scaleMatchers = new ArrayList<ColumnTypeMatcher>();
for (ColumnTypeMatcher matcher : columnTypeMatchers) {
if (matcher.matchScale(scale)) {
scaleMatchers.add(matcher);
}
}
return scaleMatchers;
}
protected ColumnTypeMatcher chooseOne(List<ColumnTypeMatcher> columnTypeMatchers) {
return columnTypeMatchers.get(0);
}
public boolean isKeyWord(String word) {
if (word == null) {
return false;
}
String wordUpper = word.toUpperCase();
for (String keyword : keywords) {
if (keyword.equals(wordUpper)) {
return true;
}
}
return false;
}
protected void setKeywords() {}
public String getEscapedKeyWord(String keyword) {
return keyword;
}
public String getCurrentTimestamp() throws DialectException {
return getCurrentDate();
}
public String getCurrentTime() throws DialectException {
return getCurrentDate();
}
public boolean needsHoldCursorsForPreparedStatement() {
return false;
}
}