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}