forked from databricks/databricks-jdbc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMetadataParser.java
More file actions
136 lines (118 loc) · 4.32 KB
/
Copy pathMetadataParser.java
File metadata and controls
136 lines (118 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.databricks.jdbc.api.impl;
import com.databricks.jdbc.exception.DatabricksDriverException;
import com.databricks.jdbc.model.telemetry.enums.DatabricksDriverErrorCode;
import java.util.LinkedHashMap;
import java.util.Map;
/** Utility class for parsing metadata descriptions into structured type mappings. */
public class MetadataParser {
/**
* Parses STRUCT metadata to extract field types.
*
* @param metadata the metadata string representing a STRUCT type
* @return a map where each key is a field name, and the value is the field's data type
*/
public static Map<String, String> parseStructMetadata(String metadata) {
Map<String, String> typeMap = new LinkedHashMap<>();
metadata = metadata.substring("STRUCT<".length(), metadata.length() - 1);
String[] fields = splitFields(metadata);
for (String field : fields) {
String[] parts = field.split(":", 2);
String fieldName = parts[0].trim();
String fieldType = cleanTypeName(parts[1].trim());
if (fieldType.startsWith("STRUCT")) {
typeMap.put(fieldName, fieldType);
} else if (fieldType.startsWith("ARRAY")) {
typeMap.put(fieldName, "ARRAY<" + parseArrayMetadata(fieldType) + ">");
} else if (fieldType.startsWith("MAP")) {
typeMap.put(fieldName, "MAP<" + parseMapMetadata(fieldType) + ">");
} else {
typeMap.put(fieldName, fieldType);
}
}
return typeMap;
}
/**
* Parses ARRAY metadata to retrieve the element type.
*
* @param metadata the metadata string representing an ARRAY type
* @return the element type contained within the array
*/
public static String parseArrayMetadata(String metadata) {
return cleanTypeName(metadata.substring("ARRAY<".length(), metadata.length() - 1).trim());
}
/**
* Parses MAP metadata to retrieve key and value types.
*
* @param metadata the metadata string representing a MAP type
* @return a string formatted as "keyType, valueType"
* @throws DatabricksDriverException if the MAP metadata format is invalid
*/
public static String parseMapMetadata(String metadata) {
metadata = metadata.substring("MAP<".length(), metadata.length() - 1).trim();
int depth = 0;
int splitIndex = -1;
for (int i = 0; i < metadata.length(); i++) {
char ch = metadata.charAt(i);
if (ch == '<') {
depth++;
} else if (ch == '>') {
depth--;
}
if (ch == ',' && depth == 0) {
splitIndex = i;
break;
}
}
if (splitIndex == -1) {
throw new DatabricksDriverException(
"Invalid MAP metadata: " + metadata,
DatabricksDriverErrorCode.COMPLEX_DATA_TYPE_MAP_CONVERSION_ERROR);
}
String keyType = cleanTypeName(metadata.substring(0, splitIndex).trim());
String valueType = cleanTypeName(metadata.substring(splitIndex + 1).trim());
return keyType + ", " + valueType;
}
/**
* Splits fields in a STRUCT metadata string, accounting for nested types.
*
* @param metadata the STRUCT metadata string to split
* @return an array of field definitions in the STRUCT
*/
private static String[] splitFields(String metadata) {
int angleBracketDepth = 0;
int parenDepth = 0;
StringBuilder currentField = new StringBuilder();
java.util.List<String> fields = new java.util.ArrayList<>();
for (char ch : metadata.toCharArray()) {
if (ch == '<') {
angleBracketDepth++;
} else if (ch == '>') {
angleBracketDepth--;
} else if (ch == '(') {
parenDepth++;
} else if (ch == ')') {
parenDepth--;
}
// Only split on commas when we're at the top level (both depths are 0)
if (ch == ',' && angleBracketDepth == 0 && parenDepth == 0) {
String field = currentField.toString().trim();
fields.add(field);
currentField.setLength(0);
} else {
currentField.append(ch);
}
}
String finalField = currentField.toString().trim();
fields.add(finalField);
return fields.toArray(new String[0]);
}
/**
* Removes any "NOT NULL" constraints and trims the type name.
*
* @param typeName the type name to clean
* @return the cleaned type name without "NOT NULL" constraints
*/
private static String cleanTypeName(String typeName) {
return typeName.replaceAll(" NOT NULL", "").trim();
}
}