private Date convertToDate( PrimitiveCategory inputType, Converter converter, DeferredObject argument) throws HiveException { assert (converter != null); assert (argument != null); if (argument.get() == null) { return null; } Date date = new Date(); switch (inputType) { case STRING: case VARCHAR: case CHAR: String dateString = converter.convert(argument.get()).toString(); try { date = formatter.parse(dateString); } catch (ParseException e) { return null; } break; case TIMESTAMP: Timestamp ts = ((TimestampWritable) converter.convert(argument.get())).getTimestamp(); date.setTime(ts.getTime()); break; case DATE: DateWritable dw = (DateWritable) converter.convert(argument.get()); date = dw.get(); break; default: throw new UDFArgumentException( "TO_DATE() only takes STRING/TIMESTAMP/DATEWRITABLE types, got " + inputType); } return date; }
public Object set(Object o, DateWritable d) { if (d == null) { return null; } ((Date) o).setTime(d.get().getTime()); return o; }
public DateScalarSubtractDateColumn(long value, int colNum, int outputColumn) { this.colNum = colNum; this.value = new Timestamp(0); this.value.setTime(DateWritable.daysToMillis((int) value)); this.outputColumn = outputColumn; scratchTimestamp2 = new Timestamp(0); }
public IntWritable evaluate(DateWritable d) { if (d == null) { return null; } calendar.setTime(d.get()); result.set(1 + calendar.get(Calendar.MONTH)); return result; }
protected void evaluateString(ColumnVector columnVector, LongColumnVector output, int i) { BytesColumnVector bcv = (BytesColumnVector) columnVector; text.set(bcv.vector[i], bcv.start[i], bcv.length[i]); try { date.setTime(formatter.parse(text.toString()).getTime()); output.vector[i] = baseDate - DateWritable.dateToDays(date); } catch (ParseException e) { output.vector[i] = 1; output.isNull[i] = true; } }
@Test public void testDatePlusIntervalYearMonth() throws Exception { GenericUDFOPPlus udf = new GenericUDFOPPlus(); DateWritable left = new DateWritable(Date.valueOf("2001-06-15")); HiveIntervalYearMonthWritable right = new HiveIntervalYearMonthWritable(HiveIntervalYearMonth.valueOf("2-8")); ObjectInspector[] inputOIs = { PrimitiveObjectInspectorFactory.writableDateObjectInspector, PrimitiveObjectInspectorFactory.writableHiveIntervalYearMonthObjectInspector }; DeferredObject[] args = { new DeferredJavaObject(left), new DeferredJavaObject(right), }; PrimitiveObjectInspector oi = (PrimitiveObjectInspector) udf.initialize(inputOIs); Assert.assertEquals(TypeInfoFactory.dateTypeInfo, oi.getTypeInfo()); DateWritable res = (DateWritable) udf.evaluate(args); Assert.assertEquals(Date.valueOf("2004-02-15"), res.get()); }
@Override public void evaluate(VectorizedRowBatch batch) { if (childExpressions != null) { super.evaluateChildren(batch); } LongColumnVector outV = (LongColumnVector) batch.cols[outputColumn]; ColumnVector inputCol = batch.cols[this.colNum]; /* every line below this is identical for evaluateLong & evaluateString */ final int n = inputCol.isRepeating ? 1 : batch.size; int[] sel = batch.selected; if (batch.size == 0) { /* n != batch.size when isRepeating */ return; } /* true for all algebraic UDFs with no state */ outV.isRepeating = inputCol.isRepeating; switch (inputTypes[0]) { case DATE: baseDate = (int) longValue; break; case TIMESTAMP: date.setTime(longValue / 1000000); baseDate = DateWritable.dateToDays(date); break; case STRING: try { date.setTime(formatter.parse(new String(stringValue, "UTF-8")).getTime()); baseDate = DateWritable.dateToDays(date); break; } catch (Exception e) { outV.noNulls = false; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.isNull[i] = true; } } else { for (int i = 0; i < n; i++) { outV.isNull[i] = true; } } return; } } switch (inputTypes[1]) { case DATE: if (inputCol.noNulls) { outV.noNulls = true; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.vector[i] = evaluateDate(inputCol, i); } } else { for (int i = 0; i < n; i++) { outV.vector[i] = evaluateDate(inputCol, i); } } } else { // Handle case with nulls. Don't do function if the value is null, to save time, // because calling the function can be expensive. outV.noNulls = false; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { outV.vector[i] = evaluateDate(inputCol, i); } } } else { for (int i = 0; i < n; i++) { outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { outV.vector[i] = evaluateDate(inputCol, i); } } } } break; case TIMESTAMP: if (inputCol.noNulls) { outV.noNulls = true; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.vector[i] = evaluateTimestamp(inputCol, i); } } else { for (int i = 0; i < n; i++) { outV.vector[i] = evaluateTimestamp(inputCol, i); } } } else { // Handle case with nulls. Don't do function if the value is null, to save time, // because calling the function can be expensive. outV.noNulls = false; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { outV.vector[i] = evaluateTimestamp(inputCol, i); } } } else { for (int i = 0; i < n; i++) { outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { outV.vector[i] = evaluateTimestamp(inputCol, i); } } } } break; case STRING: if (inputCol.noNulls) { outV.noNulls = true; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; evaluateString(inputCol, outV, i); } } else { for (int i = 0; i < n; i++) { evaluateString(inputCol, outV, i); } } } else { // Handle case with nulls. Don't do function if the value is null, to save time, // because calling the function can be expensive. outV.noNulls = false; if (batch.selectedInUse) { for (int j = 0; j < n; j++) { int i = sel[j]; outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { evaluateString(inputCol, outV, i); } } } else { for (int i = 0; i < n; i++) { outV.isNull[i] = inputCol.isNull[i]; if (!inputCol.isNull[i]) { evaluateString(inputCol, outV, i); } } } } break; } }
protected int evaluateTimestamp(ColumnVector columnVector, int index) { LongColumnVector lcv = (LongColumnVector) columnVector; date.setTime(lcv.vector[index] / 1000000); return baseDate - DateWritable.dateToDays(date); }
/* copied over from VectorUDFTimestampFieldLong */ private TimestampWritable toTimestampWritable(long daysSinceEpoch) { Timestamp ts = new Timestamp(DateWritable.daysToMillis((int) daysSinceEpoch)); return new TimestampWritable(ts); }
/** Compare two objects with their respective ObjectInspectors. */ public static int compare( Object o1, ObjectInspector oi1, Object o2, ObjectInspector oi2, MapEqualComparer mapEqualComparer) { if (oi1.getCategory() != oi2.getCategory()) { return oi1.getCategory().compareTo(oi2.getCategory()); } if (o1 == null) { return o2 == null ? 0 : -1; } else if (o2 == null) { return 1; } switch (oi1.getCategory()) { case PRIMITIVE: { PrimitiveObjectInspector poi1 = ((PrimitiveObjectInspector) oi1); PrimitiveObjectInspector poi2 = ((PrimitiveObjectInspector) oi2); if (poi1.getPrimitiveCategory() != poi2.getPrimitiveCategory()) { return poi1.getPrimitiveCategory().compareTo(poi2.getPrimitiveCategory()); } switch (poi1.getPrimitiveCategory()) { case VOID: return 0; case BOOLEAN: { int v1 = ((BooleanObjectInspector) poi1).get(o1) ? 1 : 0; int v2 = ((BooleanObjectInspector) poi2).get(o2) ? 1 : 0; return v1 - v2; } case BYTE: { int v1 = ((ByteObjectInspector) poi1).get(o1); int v2 = ((ByteObjectInspector) poi2).get(o2); return v1 - v2; } case SHORT: { int v1 = ((ShortObjectInspector) poi1).get(o1); int v2 = ((ShortObjectInspector) poi2).get(o2); return v1 - v2; } case INT: { int v1 = ((IntObjectInspector) poi1).get(o1); int v2 = ((IntObjectInspector) poi2).get(o2); return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); } case LONG: { long v1 = ((LongObjectInspector) poi1).get(o1); long v2 = ((LongObjectInspector) poi2).get(o2); return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); } case FLOAT: { float v1 = ((FloatObjectInspector) poi1).get(o1); float v2 = ((FloatObjectInspector) poi2).get(o2); return Float.compare(v1, v2); } case DOUBLE: { double v1 = ((DoubleObjectInspector) poi1).get(o1); double v2 = ((DoubleObjectInspector) poi2).get(o2); return Double.compare(v1, v2); } case STRING: { if (poi1.preferWritable() || poi2.preferWritable()) { Text t1 = (Text) poi1.getPrimitiveWritableObject(o1); Text t2 = (Text) poi2.getPrimitiveWritableObject(o2); return t1 == null ? (t2 == null ? 0 : -1) : (t2 == null ? 1 : t1.compareTo(t2)); } else { String s1 = (String) poi1.getPrimitiveJavaObject(o1); String s2 = (String) poi2.getPrimitiveJavaObject(o2); return s1 == null ? (s2 == null ? 0 : -1) : (s2 == null ? 1 : s1.compareTo(s2)); } } case CHAR: { HiveCharWritable t1 = ((HiveCharObjectInspector) poi1).getPrimitiveWritableObject(o1); HiveCharWritable t2 = ((HiveCharObjectInspector) poi2).getPrimitiveWritableObject(o2); return t1.compareTo(t2); } case VARCHAR: { HiveVarcharWritable t1 = ((HiveVarcharObjectInspector) poi1).getPrimitiveWritableObject(o1); HiveVarcharWritable t2 = ((HiveVarcharObjectInspector) poi2).getPrimitiveWritableObject(o2); return t1.compareTo(t2); } case BINARY: { BytesWritable bw1 = ((BinaryObjectInspector) poi1).getPrimitiveWritableObject(o1); BytesWritable bw2 = ((BinaryObjectInspector) poi2).getPrimitiveWritableObject(o2); return bw1.compareTo(bw2); } case DATE: { DateWritable d1 = ((DateObjectInspector) poi1).getPrimitiveWritableObject(o1); DateWritable d2 = ((DateObjectInspector) poi2).getPrimitiveWritableObject(o2); return d1.compareTo(d2); } case TIMESTAMP: { TimestampWritable t1 = ((TimestampObjectInspector) poi1).getPrimitiveWritableObject(o1); TimestampWritable t2 = ((TimestampObjectInspector) poi2).getPrimitiveWritableObject(o2); return t1.compareTo(t2); } case INTERVAL_YEAR_MONTH: { HiveIntervalYearMonthWritable i1 = ((HiveIntervalYearMonthObjectInspector) poi1).getPrimitiveWritableObject(o1); HiveIntervalYearMonthWritable i2 = ((HiveIntervalYearMonthObjectInspector) poi2).getPrimitiveWritableObject(o2); return i1.compareTo(i2); } case INTERVAL_DAY_TIME: { HiveIntervalDayTimeWritable i1 = ((HiveIntervalDayTimeObjectInspector) poi1).getPrimitiveWritableObject(o1); HiveIntervalDayTimeWritable i2 = ((HiveIntervalDayTimeObjectInspector) poi2).getPrimitiveWritableObject(o2); return i1.compareTo(i2); } case DECIMAL: { HiveDecimalWritable t1 = ((HiveDecimalObjectInspector) poi1).getPrimitiveWritableObject(o1); HiveDecimalWritable t2 = ((HiveDecimalObjectInspector) poi2).getPrimitiveWritableObject(o2); return t1.compareTo(t2); } default: { throw new RuntimeException("Unknown type: " + poi1.getPrimitiveCategory()); } } } case STRUCT: { StructObjectInspector soi1 = (StructObjectInspector) oi1; StructObjectInspector soi2 = (StructObjectInspector) oi2; List<? extends StructField> fields1 = soi1.getAllStructFieldRefs(); List<? extends StructField> fields2 = soi2.getAllStructFieldRefs(); int minimum = Math.min(fields1.size(), fields2.size()); for (int i = 0; i < minimum; i++) { int r = compare( soi1.getStructFieldData(o1, fields1.get(i)), fields1.get(i).getFieldObjectInspector(), soi2.getStructFieldData(o2, fields2.get(i)), fields2.get(i).getFieldObjectInspector(), mapEqualComparer); if (r != 0) { return r; } } return fields1.size() - fields2.size(); } case LIST: { ListObjectInspector loi1 = (ListObjectInspector) oi1; ListObjectInspector loi2 = (ListObjectInspector) oi2; int minimum = Math.min(loi1.getListLength(o1), loi2.getListLength(o2)); for (int i = 0; i < minimum; i++) { int r = compare( loi1.getListElement(o1, i), loi1.getListElementObjectInspector(), loi2.getListElement(o2, i), loi2.getListElementObjectInspector(), mapEqualComparer); if (r != 0) { return r; } } return loi1.getListLength(o1) - loi2.getListLength(o2); } case MAP: { if (mapEqualComparer == null) { throw new RuntimeException("Compare on map type not supported!"); } else { return mapEqualComparer.compare( o1, (MapObjectInspector) oi1, o2, (MapObjectInspector) oi2); } } case UNION: { UnionObjectInspector uoi1 = (UnionObjectInspector) oi1; UnionObjectInspector uoi2 = (UnionObjectInspector) oi2; byte tag1 = uoi1.getTag(o1); byte tag2 = uoi2.getTag(o2); if (tag1 != tag2) { return tag1 - tag2; } return compare( uoi1.getField(o1), uoi1.getObjectInspectors().get(tag1), uoi2.getField(o2), uoi2.getObjectInspectors().get(tag2), mapEqualComparer); } default: throw new RuntimeException("Compare on unknown type: " + oi1.getCategory()); } }
@Override /** * Method to evaluate scalar-column operation in vectorized fashion. * * @batch a package of rows with each column stored in a vector */ public void evaluate(VectorizedRowBatch batch) { if (childExpressions != null) { super.evaluateChildren(batch); } // Input #2 is type date (epochDays). LongColumnVector inputColVector2 = (LongColumnVector) batch.cols[colNum]; // Output is type HiveIntervalDayTime. IntervalDayTimeColumnVector outputColVector = (IntervalDayTimeColumnVector) batch.cols[outputColumn]; int[] sel = batch.selected; boolean[] inputIsNull = inputColVector2.isNull; boolean[] outputIsNull = outputColVector.isNull; outputColVector.noNulls = inputColVector2.noNulls; outputColVector.isRepeating = inputColVector2.isRepeating; int n = batch.size; long[] vector2 = inputColVector2.vector; // return immediately if batch is empty if (n == 0) { return; } if (inputColVector2.isRepeating) { scratchTimestamp2.setTime(DateWritable.daysToMillis((int) vector2[0])); dtm.subtract(value, scratchTimestamp2, outputColVector.getScratchIntervalDayTime()); outputColVector.setFromScratchIntervalDayTime(0); // Even if there are no nulls, we always copy over entry 0. Simplifies code. outputIsNull[0] = inputIsNull[0]; } else if (inputColVector2.noNulls) { if (batch.selectedInUse) { for (int j = 0; j != n; j++) { int i = sel[j]; scratchTimestamp2.setTime(DateWritable.daysToMillis((int) vector2[i])); dtm.subtract(value, scratchTimestamp2, outputColVector.getScratchIntervalDayTime()); outputColVector.setFromScratchIntervalDayTime(i); } } else { for (int i = 0; i != n; i++) { scratchTimestamp2.setTime(DateWritable.daysToMillis((int) vector2[i])); dtm.subtract(value, scratchTimestamp2, outputColVector.getScratchIntervalDayTime()); outputColVector.setFromScratchIntervalDayTime(i); } } } else { /* there are nulls */ if (batch.selectedInUse) { for (int j = 0; j != n; j++) { int i = sel[j]; scratchTimestamp2.setTime(DateWritable.daysToMillis((int) vector2[i])); dtm.subtract(value, scratchTimestamp2, outputColVector.getScratchIntervalDayTime()); outputColVector.setFromScratchIntervalDayTime(i); outputIsNull[i] = inputIsNull[i]; } } else { for (int i = 0; i != n; i++) { scratchTimestamp2.setTime(DateWritable.daysToMillis((int) vector2[i])); dtm.subtract(value, scratchTimestamp2, outputColVector.getScratchIntervalDayTime()); outputColVector.setFromScratchIntervalDayTime(i); } System.arraycopy(inputIsNull, 0, outputIsNull, 0, n); } } NullUtil.setNullOutputEntriesColScalar(outputColVector, batch.selectedInUse, sel, n); }
static Object deserialize(InputByteBuffer buffer, TypeInfo type, boolean invert, Object reuse) throws IOException { // Is this field a null? byte isNull = buffer.read(invert); if (isNull == 0) { return null; } assert (isNull == 1); switch (type.getCategory()) { case PRIMITIVE: { PrimitiveTypeInfo ptype = (PrimitiveTypeInfo) type; switch (ptype.getPrimitiveCategory()) { case VOID: { return null; } case BOOLEAN: { BooleanWritable r = reuse == null ? new BooleanWritable() : (BooleanWritable) reuse; byte b = buffer.read(invert); assert (b == 1 || b == 2); r.set(b == 2); return r; } case BYTE: { ByteWritable r = reuse == null ? new ByteWritable() : (ByteWritable) reuse; r.set((byte) (buffer.read(invert) ^ 0x80)); return r; } case SHORT: { ShortWritable r = reuse == null ? new ShortWritable() : (ShortWritable) reuse; int v = buffer.read(invert) ^ 0x80; v = (v << 8) + (buffer.read(invert) & 0xff); r.set((short) v); return r; } case INT: { IntWritable r = reuse == null ? new IntWritable() : (IntWritable) reuse; int v = buffer.read(invert) ^ 0x80; for (int i = 0; i < 3; i++) { v = (v << 8) + (buffer.read(invert) & 0xff); } r.set(v); return r; } case LONG: { LongWritable r = reuse == null ? new LongWritable() : (LongWritable) reuse; long v = buffer.read(invert) ^ 0x80; for (int i = 0; i < 7; i++) { v = (v << 8) + (buffer.read(invert) & 0xff); } r.set(v); return r; } case FLOAT: { FloatWritable r = reuse == null ? new FloatWritable() : (FloatWritable) reuse; int v = 0; for (int i = 0; i < 4; i++) { v = (v << 8) + (buffer.read(invert) & 0xff); } if ((v & (1 << 31)) == 0) { // negative number, flip all bits v = ~v; } else { // positive number, flip the first bit v = v ^ (1 << 31); } r.set(Float.intBitsToFloat(v)); return r; } case DOUBLE: { DoubleWritable r = reuse == null ? new DoubleWritable() : (DoubleWritable) reuse; long v = 0; for (int i = 0; i < 8; i++) { v = (v << 8) + (buffer.read(invert) & 0xff); } if ((v & (1L << 63)) == 0) { // negative number, flip all bits v = ~v; } else { // positive number, flip the first bit v = v ^ (1L << 63); } r.set(Double.longBitsToDouble(v)); return r; } case STRING: { Text r = reuse == null ? new Text() : (Text) reuse; // Get the actual length first int start = buffer.tell(); int length = 0; do { byte b = buffer.read(invert); if (b == 0) { // end of string break; } if (b == 1) { // the last char is an escape char. read the actual char buffer.read(invert); } length++; } while (true); if (length == buffer.tell() - start) { // No escaping happened, so we are already done. r.set(buffer.getData(), start, length); } else { // Escaping happened, we need to copy byte-by-byte. // 1. Set the length first. r.set(buffer.getData(), start, length); // 2. Reset the pointer. buffer.seek(start); // 3. Copy the data. byte[] rdata = r.getBytes(); for (int i = 0; i < length; i++) { byte b = buffer.read(invert); if (b == 1) { // The last char is an escape char, read the actual char. // The serialization format escape \0 to \1, and \1 to \2, // to make sure the string is null-terminated. b = (byte) (buffer.read(invert) - 1); } rdata[i] = b; } // 4. Read the null terminator. byte b = buffer.read(invert); assert (b == 0); } return r; } case BINARY: { BytesWritable bw = new BytesWritable(); // Get the actual length first int start = buffer.tell(); int length = 0; do { byte b = buffer.read(invert); if (b == 0) { // end of string break; } if (b == 1) { // the last char is an escape char. read the actual char buffer.read(invert); } length++; } while (true); if (length == buffer.tell() - start) { // No escaping happened, so we are already done. bw.set(buffer.getData(), start, length); } else { // Escaping happened, we need to copy byte-by-byte. // 1. Set the length first. bw.set(buffer.getData(), start, length); // 2. Reset the pointer. buffer.seek(start); // 3. Copy the data. byte[] rdata = bw.getBytes(); for (int i = 0; i < length; i++) { byte b = buffer.read(invert); if (b == 1) { // The last char is an escape char, read the actual char. // The serialization format escape \0 to \1, and \1 to \2, // to make sure the string is null-terminated. b = (byte) (buffer.read(invert) - 1); } rdata[i] = b; } // 4. Read the null terminator. byte b = buffer.read(invert); assert (b == 0); } return bw; } case DATE: { DateWritable d = reuse == null ? new DateWritable() : (DateWritable) reuse; long v = buffer.read(invert) ^ 0x80; for (int i = 0; i < 7; i++) { v = (v << 8) + (buffer.read(invert) & 0xff); } d.set(DateWritable.timeToDate(v)); return d; } case TIMESTAMP: TimestampWritable t = (reuse == null ? new TimestampWritable() : (TimestampWritable) reuse); byte[] bytes = new byte[8]; for (int i = 0; i < bytes.length; i++) { bytes[i] = buffer.read(invert); } t.setBinarySortable(bytes, 0); return t; default: { throw new RuntimeException("Unrecognized type: " + ptype.getPrimitiveCategory()); } } } case LIST: { ListTypeInfo ltype = (ListTypeInfo) type; TypeInfo etype = ltype.getListElementTypeInfo(); // Create the list if needed ArrayList<Object> r = reuse == null ? new ArrayList<Object>() : (ArrayList<Object>) reuse; // Read the list int size = 0; while (true) { int more = buffer.read(invert); if (more == 0) { // \0 to terminate break; } // \1 followed by each element assert (more == 1); if (size == r.size()) { r.add(null); } r.set(size, deserialize(buffer, etype, invert, r.get(size))); size++; } // Remove additional elements if the list is reused while (r.size() > size) { r.remove(r.size() - 1); } return r; } case MAP: { MapTypeInfo mtype = (MapTypeInfo) type; TypeInfo ktype = mtype.getMapKeyTypeInfo(); TypeInfo vtype = mtype.getMapValueTypeInfo(); // Create the map if needed Map<Object, Object> r; if (reuse == null) { r = new HashMap<Object, Object>(); } else { r = (HashMap<Object, Object>) reuse; r.clear(); } while (true) { int more = buffer.read(invert); if (more == 0) { // \0 to terminate break; } // \1 followed by each key and then each value assert (more == 1); Object k = deserialize(buffer, ktype, invert, null); Object v = deserialize(buffer, vtype, invert, null); r.put(k, v); } return r; } case STRUCT: { StructTypeInfo stype = (StructTypeInfo) type; List<TypeInfo> fieldTypes = stype.getAllStructFieldTypeInfos(); int size = fieldTypes.size(); // Create the struct if needed ArrayList<Object> r = reuse == null ? new ArrayList<Object>(size) : (ArrayList<Object>) reuse; assert (r.size() <= size); // Set the size of the struct while (r.size() < size) { r.add(null); } // Read one field by one field for (int eid = 0; eid < size; eid++) { r.set(eid, deserialize(buffer, fieldTypes.get(eid), invert, r.get(eid))); } return r; } case UNION: { UnionTypeInfo utype = (UnionTypeInfo) type; StandardUnion r = reuse == null ? new StandardUnion() : (StandardUnion) reuse; // Read the tag byte tag = buffer.read(invert); r.setTag(tag); r.setObject( deserialize(buffer, utype.getAllUnionObjectTypeInfos().get(tag), invert, null)); return r; } default: { throw new RuntimeException("Unrecognized type: " + type.getCategory()); } } }