/** * 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; import com.foundationdb.server.types.common.types.StringAttribute; import com.foundationdb.server.types.mcompat.mtypes.MString; import com.foundationdb.server.types.value.*; import com.foundationdb.server.types.value.UnderlyingType; import com.foundationdb.util.AkibanAppender; import com.foundationdb.util.Strings; public abstract class TClassBase extends TClass { private final TParser parser; private final int defaultVarcharLen; protected <A extends Enum<A> & Attribute> TClassBase(TBundleID bundle, String name, Enum<?> category, Class<A> enumClass, TClassFormatter formatter, int internalRepVersion, int sVersion, int sSize, UnderlyingType underlyingType, TParser parser, int defaultVarcharLen) { super(bundle, name, category, enumClass, formatter, internalRepVersion, sVersion, sSize, underlyingType); this.parser = parser; this.defaultVarcharLen = defaultVarcharLen; } @Override public void fromObject(TExecutionContext context, ValueSource in, ValueTarget out) { if (in.isNull()) out.putNull(); else if (!tryFromObject(context, in, out)) parser.parse(context, in, out); } @Override public TCast castToVarchar() { return new TCastBase(this, MString.VARCHAR) { @Override protected void doEvaluate(TExecutionContext context, ValueSource source, ValueTarget target) { AkibanAppender appender = (AkibanAppender) context.exectimeObjectAt(APPENDER_CACHE_INDEX); StringBuilder sb; if (appender == null) { sb = new StringBuilder(); appender = AkibanAppender.of(sb); context.putExectimeObject(APPENDER_CACHE_INDEX, appender); } else { sb = (StringBuilder) appender.getAppendable(); sb.setLength(0); } format(source.getType(), source, appender); String string = sb.toString(); int maxlen = context.outputType().attribute(StringAttribute.MAX_LENGTH); String trunc = Strings.truncateIfNecessary(string, maxlen); if (string != trunc) { context.reportTruncate(string, trunc); string = trunc; } target.putString(string, null); } @Override public TInstance preferredTarget(TPreptimeValue source) { int len; if (source.value() == null) { len = defaultVarcharLen; } else { StringBuilder sb = new StringBuilder(); format(source.type(), source.value(), AkibanAppender.of(sb)); len = sb.length(); } return MString.VARCHAR.instance(len, source.isNullable()); } }; } @Override public TCast castFromVarchar() { return new TCastBase(MString.VARCHAR, this) { @Override protected void doEvaluate(TExecutionContext context, ValueSource source, ValueTarget target) { parser.parse(context, source, target); } }; } protected boolean tryFromObject(TExecutionContext context, ValueSource in, ValueTarget out) { if (in.getType().equalsExcludingNullable(out.getType())) { ValueTargets.copyFrom(in, out); return true; } UnderlyingType underlyingType = TInstance.underlyingType(in.getType()); if (underlyingType == UnderlyingType.STRING || underlyingType == UnderlyingType.BYTES) return false; final String asString; switch (underlyingType) { case BOOL: asString = Boolean.toString(in.getBoolean()); break; case INT_8: asString = Byte.toString(in.getInt8()); break; case INT_16: asString = Short.toString(in.getInt16()); break; case UINT_16: asString = Integer.toString(in.getUInt16()); break; case INT_32: asString = Integer.toString(in.getInt32()); break; case INT_64: asString = Long.toString(in.getInt64()); break; case FLOAT: asString = Float.toString(in.getFloat()); break; case DOUBLE: asString = Double.toString(in.getDouble()); break; case BYTES: case STRING: default: throw new AssertionError(underlyingType + ": " + in); } parser.parse(context, new Value(MString.varcharFor(asString), asString), out); return true; } private static final int APPENDER_CACHE_INDEX = 0; }