/* * 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.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A factory for generating Dialect instances. * * @author Decebal Suiu * @author Mihai Dinca-Panaitescu */ public class DialectFactory { private static final String DATABASE = "next.dialect.database"; private static final String DIALECT = "next.dialect.class"; private static Log LOG = LogFactory.getLog(DialectFactory.class); private static final Map<String,DialectMapper> MAPPERS = new HashMap<String,DialectMapper>(); public static final String ORACLE = "Oracle"; public static final String SYBASE9 = "Adaptive Server Anywhere"; public static final String MSSQL = "Microsoft SQL Server"; public static final String MySQL = "MySQL"; public static final String DERBY = "Apache Derby"; public static final String POSTGRES = "PostgreSQL"; public static final String FIREBIRD = "Firebird"; public static final String SQLITE = "SQLite"; public static final String CSV = "CsvJdbc"; public static final String VERTICA = "Vertica Database"; public static final String PERVASIVE = "Pervasive.SQL"; public static final String TERADATA = "Teradata"; public static final String MSACCESS = "Ucanaccess"; public static final String HSQLDB = "HSQL Database Engine"; static { // add buit-in dialect mappers addDialect(ORACLE, OracleDialect.class.getName()); addDialect(SYBASE9, MSSQLDialect.class.getName()); addDialect(MSSQL, MSSQLDialect.class.getName()); addDialect(MySQL, MySQLDialect.class.getName()); addDialect(DERBY, DerbyDialect.class.getName()); addDialect(POSTGRES, PostrgreSQLDialect.class.getName()); addDialect(SQLITE, SQLiteDialect.class.getName()); addDialect(CSV, CSVDialect.class.getName()); addDialect(VERTICA, VerticaDialect.class.getName()); addDialect(PERVASIVE, PervasiveDialect.class.getName()); addDialect(TERADATA, TeradataDialect.class.getName()); addDialect(MSACCESS, MSAccessDialect.class.getName()); addDialect(HSQLDB, HSQLDBDialect.class.getName()); addDialectsFromVMParameters(); } /** * Determine the appropriate Dialect to use given the database product name * and major version. * * @param databaseName The name of the database product (obtained from metadata). * @param databaseMajorVersion The major version of the database product (obtained from metadata). * * @return An appropriate dialect instance. * @throws DialectException */ public static Dialect determineDialect(String databaseName, String databaseMajorVersion) throws DialectException { if (databaseName == null) { throw new DialectException("Dialect must be explicitly set"); } String dialectName; // TODO workaround for firebird (databaseName='Firebird 2.0.LI-V2.0.3.12981 Firebird 2.0/tcp (decebal)/P10') if (databaseName.startsWith(FIREBIRD)) { dialectName = FirebirdDialect.class.getName(); } else if (databaseName.startsWith(MSACCESS)) { dialectName = MSAccessDialect.class.getName(); } else { DialectMapper mapper = MAPPERS.get(databaseName); if (mapper == null) { throw new DialectException( "Dialect must be explicitly set for database: " + databaseName ); } dialectName = mapper.getDialectClass(databaseMajorVersion); } return buildDialect(dialectName); } /** * Returns a dialect instance given the name of the class to use. * * @param dialectName The name of the dialect class. * * @return The dialect instance. * @throws DialectException */ public static Dialect buildDialect(String dialectName) throws DialectException { try { return (Dialect) loadDialect(dialectName).newInstance(); } catch (ClassNotFoundException e) { throw new DialectException("Dialect class not found: " + dialectName); } catch (Exception e) { throw new DialectException("Could not instantiate dialect class", e); } } private static Class loadDialect(String dialectName) throws ClassNotFoundException { try { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != null) { return contextClassLoader.loadClass(dialectName); } else { return Class.forName(dialectName); } } catch (Exception e) { return Class.forName(dialectName); } } /** * For a given database product name, instances of DialectMapper know * which Dialect to use for different versions. */ public static interface DialectMapper { public String getDialectClass(String majorVersion); } /** * A simple DialectMapper for dialects which are independent * of the underlying database product version. */ public static class VersionInsensitiveMapper implements DialectMapper { private String dialectClassName; public VersionInsensitiveMapper(String dialectClassName) { this.dialectClassName = dialectClassName; } public String getDialectClass(String majorVersion) { return dialectClassName; } } /** * Add dialect * @param dialectName dialect name * @param dialectClass dialect class */ public static void addDialect(String dialectName, String dialectClass) { MAPPERS.put(dialectName, new VersionInsensitiveMapper(dialectClass)); LOG.info("Dialect added: " + dialectName + " = " + dialectClass); } // add dialects specified as VM parameters // // -Dnext.dialect.database_1="Metadata database name" (DataBaseMetaData.getDatabaseProductName()) // -Dnext.dialect.class_1="com.my.MyDialect" // With _<n> we can specify any number of dialects private static void addDialectsFromVMParameters() { Properties properties = System.getProperties(); for (Object key : properties.keySet()) { if (key instanceof String) { String name = (String) key; if (name.startsWith(DATABASE)) { String databaseName = properties.getProperty(name); int index = name.indexOf("_"); if (index > 0) { String suffix = name.substring(index); String dialectClass = properties.getProperty(DIALECT + suffix); if (dialectClass != null) { addDialect(databaseName, dialectClass); } } } } } } }