001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.dialect; 019 020import java.sql.DatabaseMetaData; 021import java.sql.SQLException; 022import java.util.Set; 023import java.util.regex.Pattern; 024 025import net.sf.hajdbc.IdentifierNormalizer; 026 027public class StandardIdentifierNormalizer implements IdentifierNormalizer 028{ 029 private static final Pattern UPPER_CASE_PATTERN = Pattern.compile("[A-Z]"); 030 private static final Pattern LOWER_CASE_PATTERN = Pattern.compile("[a-z]"); 031 032 private final Pattern identifierPattern; 033 private final Set<String> reservedIdentifiers; 034 private final String quote; 035 private final boolean supportsMixedCaseIdentifiers; 036 private final boolean supportsMixedCaseQuotedIdentifiers; 037 private final boolean storesLowerCaseIdentifiers; 038 private final boolean storesLowerCaseQuotedIdentifiers; 039 private final boolean storesUpperCaseIdentifiers; 040 private final boolean storesUpperCaseQuotedIdentifiers; 041 042 public StandardIdentifierNormalizer(DatabaseMetaData metaData, Pattern identifierPattern, Set<String> reservedIdentifiers) throws SQLException 043 { 044 this.identifierPattern = identifierPattern; 045 this.reservedIdentifiers = reservedIdentifiers; 046 this.quote = metaData.getIdentifierQuoteString(); 047 this.supportsMixedCaseIdentifiers = metaData.supportsMixedCaseIdentifiers(); 048 this.supportsMixedCaseQuotedIdentifiers = metaData.supportsMixedCaseQuotedIdentifiers(); 049 this.storesLowerCaseIdentifiers = metaData.storesLowerCaseIdentifiers(); 050 this.storesLowerCaseQuotedIdentifiers = metaData.storesLowerCaseQuotedIdentifiers(); 051 this.storesUpperCaseIdentifiers = metaData.storesUpperCaseIdentifiers(); 052 this.storesUpperCaseQuotedIdentifiers = metaData.storesUpperCaseQuotedIdentifiers(); 053 } 054 055 @Override 056 public String normalize(String identifier) 057 { 058 if (identifier == null) return null; 059 060 int quoteLength = this.quote.length(); 061 062 boolean quoted = identifier.startsWith(this.quote) && identifier.endsWith(this.quote); 063 // Strip any existing quoting 064 String raw = quoted ? identifier.substring(quoteLength, identifier.length() - quoteLength) : identifier; 065 066 // Quote reserved identifiers 067 boolean requiresQuoting = this.reservedIdentifiers.contains(raw.toUpperCase()); 068 069 // Quote identifiers containing special characters 070 requiresQuoting |= !this.identifierPattern.matcher(raw).matches(); 071 072 // Quote mixed-case identifiers if detected and supported by DBMS 073 requiresQuoting |= quoted && !this.supportsMixedCaseIdentifiers && this.supportsMixedCaseQuotedIdentifiers && ((this.storesLowerCaseIdentifiers && !this.storesLowerCaseQuotedIdentifiers && UPPER_CASE_PATTERN.matcher(raw).find()) || (this.storesUpperCaseIdentifiers && !this.storesUpperCaseQuotedIdentifiers && LOWER_CASE_PATTERN.matcher(raw).find())); 074 075 return requiresQuoting ? this.quote + this.normalizeCaseQuoted(raw) + this.quote : this.normalizeCase(raw); 076 } 077 078 private String normalizeCase(String identifier) 079 { 080 if (this.storesLowerCaseIdentifiers) return identifier.toLowerCase(); 081 082 if (this.storesUpperCaseIdentifiers) return identifier.toUpperCase(); 083 084 return identifier; 085 } 086 087 private String normalizeCaseQuoted(String identifier) 088 { 089 if (this.storesLowerCaseQuotedIdentifiers) return identifier.toLowerCase(); 090 091 if (this.storesUpperCaseQuotedIdentifiers) return identifier.toUpperCase(); 092 093 return identifier; 094 } 095}