com.amalto.core.storage.hibernate.StorageTableResolver.java Source code

Java tutorial

Introduction

Here is the source code for com.amalto.core.storage.hibernate.StorageTableResolver.java

Source

/*
 * Copyright (C) 2006-2016 Talend Inc. - www.talend.com
 * 
 * This source code is available under agreement available at
 * %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
 * 
 * You should have received a copy of the agreement along with this program; if not, write to Talend SA 9 rue Pages
 * 92150 Suresnes, France
 */

package com.amalto.core.storage.hibernate;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.talend.mdm.commmon.metadata.ComplexTypeMetadata;
import org.talend.mdm.commmon.metadata.FieldMetadata;
import org.talend.mdm.commmon.metadata.ReferenceFieldMetadata;

class StorageTableResolver implements TableResolver {

    private static final String RESERVED_SQL_KEYWORDS = "reservedSQLKeywords.txt"; //$NON-NLS-1$

    private static final Logger LOGGER = Logger.getLogger(StorageTableResolver.class);

    private static final String STANDARD_PREFIX = "X_"; //$NON-NLS-1$

    private static final String RESERVED_KEYWORD_PREFIX = "X_"; //$NON-NLS-1$

    private static Set<String> reservedKeyWords;

    private final Set<FieldMetadata> indexedFields;

    private final int maxLength;

    private final AtomicInteger fkIncrement = new AtomicInteger();

    private final Set<String> referenceFieldNames = new HashSet<String>();

    public StorageTableResolver(Set<FieldMetadata> indexedFields, int maxLength) {
        this.indexedFields = indexedFields;
        this.maxLength = maxLength;
        // Loads reserved SQL keywords.
        synchronized (MappingGenerator.class) {
            if (reservedKeyWords == null) {
                reservedKeyWords = new TreeSet<String>();
                InputStream reservedKeyWordsList = this.getClass().getResourceAsStream(RESERVED_SQL_KEYWORDS);
                try {
                    if (reservedKeyWordsList == null) {
                        throw new IllegalStateException("File '" + RESERVED_SQL_KEYWORDS + "' was not found.");
                    }
                    List list = IOUtils.readLines(reservedKeyWordsList);
                    for (Object o : list) {
                        reservedKeyWords.add(String.valueOf(o));
                    }
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Loaded " + reservedKeyWords.size() + " reserved SQL key words.");
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        if (reservedKeyWordsList != null) {
                            reservedKeyWordsList.close();
                        }
                    } catch (IOException e) {
                        LOGGER.error("Error occurred when closing reserved keyword list.", e);
                    }
                }
            }
        }
    }

    @Override
    public String get(ComplexTypeMetadata type) {
        String tableName = formatSQLName(type.getName().replace('-', '_'));
        if (!type.isInstantiable() && !tableName.startsWith(STANDARD_PREFIX)) {
            tableName = STANDARD_PREFIX + tableName;
        }
        return formatSQLName(tableName.replace('-', '_'));
    }

    @Override
    public String get(FieldMetadata field) {
        return get(field, StringUtils.EMPTY);
    }

    @Override
    public String get(FieldMetadata field, String prefix) {
        String name;
        if (StringUtils.isEmpty(prefix)) {
            name = field.getName();
        } else {
            name = prefix + '_' + field.getName();
        }
        String formattedName = formatSQLName(name.replace('-', '_'));
        if (!formattedName.startsWith(STANDARD_PREFIX)
                && !formattedName.startsWith(STANDARD_PREFIX.toLowerCase())) {
            return (STANDARD_PREFIX + formattedName).toLowerCase();
        }
        return formattedName.toLowerCase();
    }

    @Override
    public boolean isIndexed(FieldMetadata field) {
        return indexedFields.contains(field);
    }

    @Override
    public String getIndex(String fieldName, String prefix) {
        return formatSQLName(prefix + '_' + fieldName + "_index"); //$NON-NLS-1$
    }

    @Override
    public String getCollectionTable(FieldMetadata field) {
        if (field instanceof ReferenceFieldMetadata) {
            ReferenceFieldMetadata referenceField = (ReferenceFieldMetadata) field;
            return formatSQLName(referenceField.getContainingType().getName() + '_' + referenceField.getName() + '_'
                    + referenceField.getReferencedType().getName());
        }
        return formatSQLName(get(field.getContainingType()) + '_' + field.getName());
    }

    @Override
    public String getFkConstraintName(ReferenceFieldMetadata referenceField) {
        // TMDM-6896 Uses containing type length since FK collision issues happens when same FK is contained in a type
        // with same
        // length but different name.
        if (!referenceFieldNames
                .add(referenceField.getContainingType().getName().length() + '_' + referenceField.getName())) {
            return formatSQLName(
                    "FK_" + Math.abs(referenceField.getName().hashCode()) + fkIncrement.incrementAndGet()); //$NON-NLS-1$
        } else {
            return StringUtils.EMPTY;
        }
    }

    @Override
    public String get(String name) {
        return formatSQLName(name);
    }

    /**
     * <p>
     * Short a string so it doesn't exceed <code>maxLength</code> length. Consecutive calls to this method with same
     * input always return the same value.
     * </p>
     * <p>
     * This method also makes sure the SQL name is not a reserved SQL key word.
     * </p>
     * <p>
     * Additionally, this method will replace all '-' characters by '_' in the returned string.
     * </p>
     * 
     * @param s A non null string.
     * @return <code>null</code> if <code>s</code> is null, a shorten string so it doesn't exceed <code>maxLength</code>
     * .
     */
    protected String formatSQLName(String s) {
        if (maxLength < 1) {
            throw new IllegalArgumentException("Max length must be greater than 0 (was " + maxLength + ").");
        }
        if (s == null) {
            return null;
        }
        // Adds a prefix until 's' is no longer a SQL reserved key word.
        String backup = s;
        while (reservedKeyWords.contains(s.toUpperCase())) {
            s = RESERVED_KEYWORD_PREFIX + s;
        }
        if (LOGGER.isDebugEnabled()) {
            if (!s.equals(backup)) {
                LOGGER.debug("Replaced '" + backup + "' with '" + s + "' because it is a reserved SQL keyword.");
            }
        }
        if (s.length() < maxLength) {
            return s;
        }
        char[] chars = s.toCharArray();
        String shortString = __shortString(chars, maxLength);
        while (shortString.length() > maxLength) {
            shortString = __shortString(shortString.toCharArray(), maxLength);
        }

        return shortString;
    }

    // Internal method for recursion.
    private static String __shortString(char[] chars, int threshold) {
        if (chars.length < threshold) {
            return new String(chars).replace('-', '_');
        } else {
            String s = new String(ArrayUtils.subarray(chars, 0, threshold / 2))
                    + new String(ArrayUtils.subarray(chars, threshold / 2, chars.length)).hashCode();
            return __shortString(s.toCharArray(), threshold);
        }
    }
}