Skip to content

Commit c3a9542

Browse files
authored
feat: Added BQSchemaToProtoDescriptor.java (#395)
Add class that converts from BQ Table Schema to Protocol Buffer Descriptor.
1 parent dab15da commit c3a9542

File tree

3 files changed

+472
-0
lines changed

3 files changed

+472
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigquery.storage.v1alpha2;
17+
18+
import com.google.common.collect.ImmutableList;
19+
import com.google.common.collect.ImmutableMap;
20+
import com.google.protobuf.DescriptorProtos.DescriptorProto;
21+
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
22+
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
23+
import com.google.protobuf.Descriptors;
24+
import com.google.protobuf.Descriptors.Descriptor;
25+
import com.google.protobuf.Descriptors.FileDescriptor;
26+
import java.util.ArrayList;
27+
import java.util.HashMap;
28+
import java.util.List;
29+
30+
/**
31+
* Converts a BQ table schema to protobuf descriptor. The mapping between field types and field
32+
* modes are shown in the ImmutableMaps below.
33+
*/
34+
public class BQTableSchemaToProtoDescriptor {
35+
private static ImmutableMap<Table.TableFieldSchema.Mode, FieldDescriptorProto.Label>
36+
BQTableSchemaModeMap =
37+
ImmutableMap.of(
38+
Table.TableFieldSchema.Mode.NULLABLE, FieldDescriptorProto.Label.LABEL_OPTIONAL,
39+
Table.TableFieldSchema.Mode.REPEATED, FieldDescriptorProto.Label.LABEL_REPEATED,
40+
Table.TableFieldSchema.Mode.REQUIRED, FieldDescriptorProto.Label.LABEL_REQUIRED);
41+
42+
private static ImmutableMap<Table.TableFieldSchema.Type, FieldDescriptorProto.Type>
43+
BQTableSchemaTypeMap =
44+
new ImmutableMap.Builder<Table.TableFieldSchema.Type, FieldDescriptorProto.Type>()
45+
.put(Table.TableFieldSchema.Type.BOOL, FieldDescriptorProto.Type.TYPE_BOOL)
46+
.put(Table.TableFieldSchema.Type.BYTES, FieldDescriptorProto.Type.TYPE_BYTES)
47+
.put(Table.TableFieldSchema.Type.DATE, FieldDescriptorProto.Type.TYPE_INT64)
48+
.put(Table.TableFieldSchema.Type.DATETIME, FieldDescriptorProto.Type.TYPE_INT64)
49+
.put(Table.TableFieldSchema.Type.DOUBLE, FieldDescriptorProto.Type.TYPE_DOUBLE)
50+
.put(Table.TableFieldSchema.Type.GEOGRAPHY, FieldDescriptorProto.Type.TYPE_BYTES)
51+
.put(Table.TableFieldSchema.Type.INT64, FieldDescriptorProto.Type.TYPE_INT64)
52+
.put(Table.TableFieldSchema.Type.NUMERIC, FieldDescriptorProto.Type.TYPE_BYTES)
53+
.put(Table.TableFieldSchema.Type.STRING, FieldDescriptorProto.Type.TYPE_STRING)
54+
.put(Table.TableFieldSchema.Type.STRUCT, FieldDescriptorProto.Type.TYPE_MESSAGE)
55+
.put(Table.TableFieldSchema.Type.TIME, FieldDescriptorProto.Type.TYPE_INT64)
56+
.put(Table.TableFieldSchema.Type.TIMESTAMP, FieldDescriptorProto.Type.TYPE_INT64)
57+
.build();
58+
59+
/**
60+
* Converts Table.TableSchema to a Descriptors.Descriptor object.
61+
*
62+
* @param BQTableSchema
63+
* @throws Descriptors.DescriptorValidationException
64+
*/
65+
public static Descriptor ConvertBQTableSchemaToProtoDescriptor(Table.TableSchema BQTableSchema)
66+
throws Descriptors.DescriptorValidationException {
67+
return ConvertBQTableSchemaToProtoDescriptorImpl(
68+
BQTableSchema, "root", new HashMap<ImmutableList<Table.TableFieldSchema>, Descriptor>());
69+
}
70+
71+
/**
72+
* Converts a Table.TableSchema to a Descriptors.Descriptor object.
73+
*
74+
* @param BQTableSchema
75+
* @param scope Keeps track of current scope to prevent repeated naming while constructing
76+
* descriptor.
77+
* @param dependencyMap Stores already constructed descriptors to prevent reconstruction
78+
* @throws Descriptors.DescriptorValidationException
79+
*/
80+
private static Descriptor ConvertBQTableSchemaToProtoDescriptorImpl(
81+
Table.TableSchema BQTableSchema,
82+
String scope,
83+
HashMap<ImmutableList<Table.TableFieldSchema>, Descriptor> dependencyMap)
84+
throws Descriptors.DescriptorValidationException {
85+
List<FileDescriptor> dependenciesList = new ArrayList<FileDescriptor>();
86+
List<FieldDescriptorProto> fields = new ArrayList<FieldDescriptorProto>();
87+
int index = 1;
88+
for (Table.TableFieldSchema BQTableField : BQTableSchema.getFieldsList()) {
89+
String currentScope = scope + "__" + BQTableField.getName();
90+
if (BQTableField.getType() == Table.TableFieldSchema.Type.STRUCT) {
91+
ImmutableList<Table.TableFieldSchema> fieldList =
92+
ImmutableList.copyOf(BQTableField.getFieldsList());
93+
if (dependencyMap.containsKey(fieldList)) {
94+
Descriptor descriptor = dependencyMap.get(fieldList);
95+
dependenciesList.add(descriptor.getFile());
96+
fields.add(ConvertBQTableFieldToProtoField(BQTableField, index++, descriptor.getName()));
97+
} else {
98+
Descriptor descriptor =
99+
ConvertBQTableSchemaToProtoDescriptorImpl(
100+
Table.TableSchema.newBuilder().addAllFields(fieldList).build(),
101+
currentScope,
102+
dependencyMap);
103+
dependenciesList.add(descriptor.getFile());
104+
dependencyMap.put(fieldList, descriptor);
105+
fields.add(ConvertBQTableFieldToProtoField(BQTableField, index++, currentScope));
106+
}
107+
} else {
108+
fields.add(ConvertBQTableFieldToProtoField(BQTableField, index++, currentScope));
109+
}
110+
}
111+
FileDescriptor[] dependenciesArray = new FileDescriptor[dependenciesList.size()];
112+
dependenciesArray = dependenciesList.toArray(dependenciesArray);
113+
DescriptorProto descriptorProto =
114+
DescriptorProto.newBuilder().setName(scope).addAllField(fields).build();
115+
FileDescriptorProto fileDescriptorProto =
116+
FileDescriptorProto.newBuilder().addMessageType(descriptorProto).build();
117+
FileDescriptor fileDescriptor =
118+
FileDescriptor.buildFrom(fileDescriptorProto, dependenciesArray);
119+
Descriptor descriptor = fileDescriptor.findMessageTypeByName(scope);
120+
return descriptor;
121+
}
122+
123+
/**
124+
* Converts a BQTableField to ProtoField
125+
*
126+
* @param BQTableField BQ Field used to construct a FieldDescriptorProto
127+
* @param index Index for protobuf fields.
128+
* @param scope used to name descriptors
129+
*/
130+
private static FieldDescriptorProto ConvertBQTableFieldToProtoField(
131+
Table.TableFieldSchema BQTableField, int index, String scope) {
132+
Table.TableFieldSchema.Mode mode = BQTableField.getMode();
133+
String fieldName = BQTableField.getName();
134+
if (BQTableField.getType() == Table.TableFieldSchema.Type.STRUCT) {
135+
return FieldDescriptorProto.newBuilder()
136+
.setName(fieldName)
137+
.setTypeName(scope)
138+
.setLabel((FieldDescriptorProto.Label) BQTableSchemaModeMap.get(mode))
139+
.setNumber(index)
140+
.build();
141+
}
142+
return FieldDescriptorProto.newBuilder()
143+
.setName(fieldName)
144+
.setType((FieldDescriptorProto.Type) BQTableSchemaTypeMap.get(BQTableField.getType()))
145+
.setLabel((FieldDescriptorProto.Label) BQTableSchemaModeMap.get(mode))
146+
.setNumber(index)
147+
.build();
148+
}
149+
}

0 commit comments

Comments
 (0)