package com.tesora.dve.db; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.tesora.dve.common.ArrayListFactory; import com.tesora.dve.common.HashMapFactory; import com.tesora.dve.common.MultiMap; import com.tesora.dve.common.TwoDimensionalMultiMap; import com.tesora.dve.db.mysql.MyFieldType; import com.tesora.dve.exceptions.PEException; public abstract class NativeTypeCatalog implements Serializable { private static final long serialVersionUID = 1L; public static final class NativeTypeByPrecisionLookup { public static enum MyFieldDataKind { ANY, BINARY, TEXT } private static final Comparator<NativeType> NATIVE_TYPE_BY_PRECISION = new Comparator<NativeType>() { @Override public int compare(NativeType a, NativeType b) { return Long.compare(a.getPrecision(), b.getPrecision()); } }; private final TwoDimensionalMultiMap<Integer, MyFieldDataKind, NativeType> myFieldLookupTable = new TwoDimensionalMultiMap<Integer, MyFieldDataKind, NativeType>( new HashMapFactory<Integer, MultiMap<MyFieldDataKind, NativeType>>(), new HashMapFactory<MyFieldDataKind, Collection<NativeType>>(), new ArrayListFactory<NativeType>()); protected NativeTypeByPrecisionLookup() { } public NativeType findSmallestSuitable(final int nativeTypeId, final MyFieldDataKind dataKind, final long typeLength, final Set<NativeType> ignored) { final List<NativeType> suitableTypes = (List<NativeType>) this.myFieldLookupTable.get(nativeTypeId, dataKind); return findSmallestSuitableType(typeLength, suitableTypes, ignored); } /** * Find the index of the first element (e) such that e.getPrecision() >= * typeLength. * If there is only one compatible type return that. * * @param types * Searched elements sorted by type precision. */ private NativeType findSmallestSuitableType(final long typeLength, final List<NativeType> types, final Set<NativeType> ignored) { final int numTypes = types.size(); if (numTypes == 1) { return types.get(0); } else if (numTypes > 1) { for (final NativeType type : types) { if (!ignored.contains(type) && (type.getPrecision() >= typeLength)) { return type; } } } return null; } private void reload(final List<NativeType> types) { Collections.sort(types, NATIVE_TYPE_BY_PRECISION); reinitializeLookupTable(types); } private void reinitializeLookupTable(final List<NativeType> types) { this.myFieldLookupTable.clear(); for (final NativeType type : types) { addTypeByNativeTypeId(type); } } private void addTypeByNativeTypeId(final NativeType type) { final int nativeTypeId = type.getNativeTypeId(); this.myFieldLookupTable.put(nativeTypeId, MyFieldDataKind.ANY, type); if (type.isBinaryType()) { this.myFieldLookupTable.put(nativeTypeId, MyFieldDataKind.BINARY, type); } if (type.isStringType()) { this.myFieldLookupTable.put(nativeTypeId, MyFieldDataKind.TEXT, type); } } } protected List<NativeType> loadedTypes; protected Map<String, NativeType> typesByName; protected MultiMap<Integer, NativeType> typesByDataType; protected List<String> jdbcTypeNames; protected NativeTypeByPrecisionLookup typesByPrecision; public NativeTypeCatalog() { loadedTypes = new ArrayList<NativeType>(); typesByName = new LinkedHashMap<String, NativeType>(); typesByDataType = new MultiMap<Integer, NativeType>(); jdbcTypeNames = new ArrayList<String>(); typesByPrecision = new NativeTypeByPrecisionLookup(); } protected void addType(NativeType nativeType) { loadedTypes.add(nativeType); typesByName.put(nativeType.getTypeName(), nativeType); if ( nativeType.isJdbcType() ) jdbcTypeNames.add(nativeType.getTypeName()); for (NativeTypeAlias alias : nativeType.getAliases()) { String aliasFixedName = NativeType.fixName(alias.getAliasName(),true); typesByName.put(aliasFixedName, nativeType); if ( alias.isJdbcType() ) jdbcTypeNames.add(aliasFixedName); } typesByDataType.put(nativeType.getDataType(), nativeType); } public void load() throws PEException { addTypes(); initializeByPrecisionLookup(new ArrayList<NativeType>(this.loadedTypes)); } protected abstract void addTypes() throws PEException; private void initializeByPrecisionLookup(final List<NativeType> types) { this.typesByPrecision.reload(types); } public NativeType findType(String name, boolean except) throws PEException { NativeType nativeType = typesByName.get(NativeType.fixName(name, true)); if (except && (nativeType == null)) { throw new PEException("Unknown type: '" + name + "'"); } return nativeType; } public NativeType findType(int dataType, boolean except) throws PEException { NativeType nativeType = null; Collection<NativeType> nativeTypes = typesByDataType.get(dataType); if (nativeTypes != null && nativeTypes.size() > 0) { nativeType = ((List<NativeType>) nativeTypes).get(0); } if (except && (nativeType == null)) { throw new PEException("Unknown data type code: " + dataType); } return nativeType; } public abstract NativeType findType(final MyFieldType mft, final int flags, final long maxLength, final boolean except) throws PEException; protected NativeType findSmallestSuitableType(final int nativeTypeId, final NativeTypeByPrecisionLookup.MyFieldDataKind dataKind, final long maxLength, final Set<NativeType> ignored) { return this.typesByPrecision.findSmallestSuitable(nativeTypeId, dataKind, maxLength, ignored); } public int size() { return this.loadedTypes.size(); } public Set<Map.Entry<String, NativeType>> getTypeCatalogEntries() { return this.typesByName.entrySet(); } public Map<String, NativeType> getTypesByName() { return this.typesByName; } public List<String> getJdbcTypeNames() { return this.jdbcTypeNames; } }