/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
package com.foundationdb.server.types.service;
import com.foundationdb.server.error.UnsupportedColumnDataTypeException;
import com.foundationdb.server.types.TBundleID;
import com.foundationdb.server.types.TClass;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.TName;
import com.foundationdb.server.types.common.types.StringAttribute;
import com.foundationdb.server.types.common.types.StringFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
public class TypesRegistry
{
private static final Logger logger = LoggerFactory.getLogger(TypesRegistry.class);
static class BundleEntry {
TBundleID bundleID;
Map<String,TClass> typeClassByName =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
BundleEntry(TBundleID bundleID) {
this.bundleID = bundleID;
}
}
// NOTE: Lookup by UUID should be used for persistence of types.
// Lookup by bundle name should only be used by tests that don't parse SQL DDL.
private final Map<UUID,BundleEntry> bundlesByUUID = new HashMap<>();
private final Map<String,BundleEntry> bundlesByName =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
public TypesRegistry(Collection<? extends TClass> tClasses) {
for (TClass tClass : tClasses) {
TName tName = tClass.name();
TBundleID bundleID = tName.bundleId();
BundleEntry entry = bundlesByUUID.get(bundleID.uuid());
if (entry == null) {
entry = new BundleEntry(bundleID);
bundlesByUUID.put(bundleID.uuid(), entry);
BundleEntry oentry = bundlesByName.put(bundleID.name(), entry);
if (oentry != null) {
// Tests may fail, but can work if only one used.
logger.warn("There is more than one bundle named {}: {} and {}",
new Object[] {
bundleID.name(), bundleID.uuid(),
oentry.bundleID.uuid()
});
}
}
TClass prev = entry.typeClassByName.put(tName.unqualifiedName(), tClass);
if (prev != null) {
throw new IllegalStateException("Duplicate classes: " + prev +
" and " + tClass);
}
}
}
public Collection<TBundleID> getTypeBundleIDs() {
Collection<TBundleID> result = new ArrayList<>(bundlesByName.size());
for (BundleEntry entry : bundlesByName.values())
result.add(entry.bundleID);
return result;
}
public Collection<TClass> getTypeClasses() {
Collection<TClass> result = new ArrayList<>();
for (BundleEntry entry : bundlesByName.values())
result.addAll(entry.typeClassByName.values());
return result;
}
public TClass getTypeClass(UUID bundleUUID, String name) {
BundleEntry entry = bundlesByUUID.get(bundleUUID);
if (entry == null)
return null;
else
return entry.typeClassByName.get(name);
}
public TClass getTypeClass(String bundleName, String name) {
BundleEntry entry = bundlesByName.get(bundleName);
if (entry == null)
return null;
else
return entry.typeClassByName.get(name);
}
/**
* Get the type instance with the given name and parameters.
* Table and column name are supplied for the sake of the error message.
*/
public TInstance getType(UUID typeBundleUUID, String typeName, int typeVersion,
Long typeParameter1, Long typeParameter2,
boolean nullable,
String tableSchema, String tableName, String columnName) {
return getType(typeBundleUUID, typeName, typeVersion,
typeParameter1, typeParameter2, null, null,
nullable, tableSchema, tableName, columnName);
}
public TInstance getType(UUID typeBundleUUID, String typeName, int typeVersion,
Long typeParameter1, Long typeParameter2,
String charset, String collation,
boolean nullable,
String tableSchema, String tableName, String columnName) {
return getType(typeBundleUUID, typeName, typeVersion,
typeParameter1, typeParameter2,
charset, collation, StringFactory.DEFAULT_CHARSET_ID, StringFactory.DEFAULT_COLLATION_ID,
nullable, tableSchema, tableName, columnName);
}
public TInstance getType(UUID typeBundleUUID, String typeName, int typeVersion,
Long typeParameter1, Long typeParameter2,
String charset, String collation,
int defaultCharsetId, int defaultCollationId,
boolean nullable,
String tableSchema, String tableName, String columnName) {
TClass typeClass = getTypeClass(typeBundleUUID, typeName);
if (typeClass == null) {
throw new UnsupportedColumnDataTypeException(tableSchema, tableName, columnName,
typeName);
}
assert (typeVersion == typeClass.serializationVersion()) : typeClass;
return getType(typeClass, typeParameter1, typeParameter2,
charset, collation, defaultCharsetId, defaultCollationId,
nullable, tableSchema, tableName, columnName);
}
/** For tests */
public TInstance getType(String bundleName, String typeName,
Long typeParameter1, Long typeParameter2,
boolean nullable,
String tableSchema, String tableName, String columnName) {
TClass typeClass = getTypeClass(bundleName, typeName);
if (typeClass == null) {
throw new UnsupportedColumnDataTypeException(tableSchema, tableName, columnName,
typeName);
}
return getType(typeClass, typeParameter1, typeParameter2, null, null, StringFactory.DEFAULT_CHARSET_ID, StringFactory.DEFAULT_COLLATION_ID,
nullable, tableSchema, tableName, columnName);
}
public TInstance getType(String bundleName, String typeName,
Long typeParameter1, Long typeParameter2,
String charset, String collation,
boolean nullable,
String tableSchema, String tableName, String columnName) {
TClass typeClass = getTypeClass(bundleName, typeName);
if (typeClass == null) {
throw new UnsupportedColumnDataTypeException(tableSchema, tableName, columnName,
typeName);
}
return getType(typeClass, typeParameter1, typeParameter2, charset, collation, StringFactory.DEFAULT_CHARSET_ID, StringFactory.DEFAULT_COLLATION_ID,
nullable, tableSchema, tableName, columnName);
}
protected TInstance getType(TClass typeClass,
Long typeParameter1, Long typeParameter2,
String charset, String collation,
int defaultCharsetId, int defaultCollationId,
boolean nullable,
String tableSchema, String tableName, String columnName) {
if (typeClass.hasAttributes(StringAttribute.class)) {
int charsetId = defaultCharsetId, collationId = defaultCollationId;
if (charset != null) {
charsetId = StringFactory.charsetNameToId(charset);
}
if (collation != null) {
collationId = StringFactory.collationNameToId(collation);
}
return typeClass.instance((typeParameter1 == null) ? 0 : typeParameter1.intValue(),
charsetId, collationId,
nullable);
}
else if (typeParameter2 != null) {
return typeClass.instance(typeParameter1.intValue(), typeParameter2.intValue(),
nullable);
}
else if (typeParameter1 != null) {
return typeClass.instance(typeParameter1.intValue(), nullable);
}
else {
return typeClass.instance(nullable);
}
}
}