/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.sqoop.json.util; import java.util.HashSet; import org.apache.sqoop.classification.InterfaceAudience; import org.apache.sqoop.classification.InterfaceStability; import org.apache.sqoop.schema.NullSchema; import org.apache.sqoop.schema.Schema; import org.apache.sqoop.schema.type.AbstractComplexListType; import org.apache.sqoop.schema.type.AbstractPrimitiveType; import org.apache.sqoop.schema.type.AbstractString; import org.apache.sqoop.schema.type.Array; import org.apache.sqoop.schema.type.Binary; import org.apache.sqoop.schema.type.Bit; import org.apache.sqoop.schema.type.Column; import org.apache.sqoop.schema.type.ColumnType; import org.apache.sqoop.schema.type.Date; import org.apache.sqoop.schema.type.DateTime; import org.apache.sqoop.schema.type.Decimal; import org.apache.sqoop.schema.type.Enum; import org.apache.sqoop.schema.type.FixedPoint; import org.apache.sqoop.schema.type.FloatingPoint; import org.apache.sqoop.schema.type.Map; import org.apache.sqoop.schema.type.Set; import org.apache.sqoop.schema.type.Text; import org.apache.sqoop.schema.type.Time; import org.apache.sqoop.schema.type.Unknown; import org.json.simple.JSONArray; import org.json.simple.JSONObject; /** * */ @InterfaceAudience.Private @InterfaceStability.Unstable public class SchemaSerialization { // common attributes of all column types private static final String NAME = "name"; private static final String CREATION_DATE = "created"; private static final String NOTE = "note"; private static final String COLUMNS = "columns"; private static final String TYPE = "type"; private static final String NULLABLE = "nullable"; // size attribute is relevant to String and Array type only private static final String SIZE = "size"; // maps and enum attributes private static final String MAP = "map"; private static final String KEY = "key"; private static final String VALUE = "value"; // arrays and set attribute private static final String LIST = "list"; private static final String LIST_TYPE = "listType"; // enum attribute private static final String ENUM_OPTIONS = "options"; // number attribute private static final String BYTE_SIZE = "byteSize"; // string attribute private static final String CHAR_SIZE = "charSize"; private static final String FRACTION = "fraction"; private static final String TIMEZONE = "timezone"; private static final String PRECISION = "precision"; private static final String SCALE = "scale"; private static final String SIGNED = "signed"; private static final String JDBC_TYPE = "jdbc-type"; @SuppressWarnings("unchecked") public static JSONObject extractSchema(Schema schema) { JSONObject object = new JSONObject(); // just a defensive check if (schema != null) { object.put(NAME, schema.getName()); object.put(CREATION_DATE, schema.getCreationDate().getTime()); if (schema.getNote() != null) { object.put(NOTE, schema.getNote()); } JSONArray columnArray = new JSONArray(); for (Column column : schema.getColumnsArray()) { columnArray.add(extractColumn(column)); } object.put(COLUMNS, columnArray); } return object; } public static Schema restoreSchema(JSONObject jsonObject) { // if the object is empty return a empty schema if (jsonObject == null || jsonObject.isEmpty()) { return NullSchema.getInstance(); } String name = (String) jsonObject.get(NAME); String note = (String) jsonObject.get(NOTE); java.util.Date date = new java.util.Date((Long) jsonObject.get(CREATION_DATE)); Schema schema = new Schema(name).setNote(note).setCreationDate(date); JSONArray columnsArray = (JSONArray) jsonObject.get(COLUMNS); for (Object obj : columnsArray) { schema.addColumn(restoreColumn((JSONObject) obj)); } return schema; } @SuppressWarnings("unchecked") private static JSONObject extractColumn(Column column) { JSONObject ret = new JSONObject(); ret.put(NAME, column.getName()); ret.put(NULLABLE, column.isNullable()); ret.put(TYPE, column.getType().name()); switch (column.getType()) { case MAP: JSONObject map = new JSONObject(); ret.put(MAP, map); map.put(KEY, extractColumn(((Map) column).getKey())); map.put(VALUE, extractColumn(((Map) column).getValue())); break; case ENUM: JSONObject enumList = new JSONObject(); ret.put(LIST, enumList); JSONArray optionsArray = new JSONArray(); optionsArray.addAll(((Enum)column).getOptions()); enumList.put(ENUM_OPTIONS, optionsArray); break; case SET: JSONObject set = new JSONObject(); ret.put(LIST, set); set.put(LIST_TYPE, extractColumn(((AbstractComplexListType) column).getListType())); break; case ARRAY: JSONObject arrayList = new JSONObject(); ret.put(LIST, arrayList); arrayList.put(SIZE, ((Array) column).getSize()); arrayList.put(LIST_TYPE, extractColumn(((Array) column).getListType())); break; case BINARY: case TEXT: ret.put(CHAR_SIZE, ((AbstractString) column).getCharSize()); break; case DATE_TIME: ret.put(FRACTION, ((DateTime) column).hasFraction()); ret.put(TIMEZONE, ((DateTime) column).hasTimezone()); break; case DECIMAL: ret.put(PRECISION, ((Decimal) column).getPrecision()); ret.put(SCALE, ((Decimal) column).getScale()); break; case FIXED_POINT: ret.put(BYTE_SIZE, ((FixedPoint) column).getByteSize()); ret.put(SIGNED, ((FixedPoint) column).isSigned()); break; case FLOATING_POINT: ret.put(BYTE_SIZE, ((FloatingPoint) column).getByteSize()); break; case TIME: ret.put(FRACTION, ((Time) column).hasFraction()); break; case UNKNOWN: ret.put(JDBC_TYPE, ((Unknown) column).getJdbcType()); break; case DATE: case BIT: break; default: // TODO(jarcec): Throw an exception of unsupported type? } return ret; } private static Column restoreColumn(JSONObject obj) { String name = (String) obj.get(NAME); Boolean nullable = (Boolean) obj.get(NULLABLE); AbstractPrimitiveType key = null; Column value = null; Long arraySize = null; Column listType = null; java.util.Set<String> options = new HashSet<String>(); // complex type attribute if (obj.containsKey(MAP)) { JSONObject map = (JSONObject) obj.get(MAP); if (map.containsKey(KEY)) { key = (AbstractPrimitiveType) restoreColumn((JSONObject) map.get(KEY)); } if (map.containsKey(VALUE)) { value = restoreColumn((JSONObject) map.get(VALUE)); } } if (obj.containsKey(LIST)) { JSONObject list = (JSONObject) obj.get(LIST); if (list.containsKey(LIST_TYPE)) { listType = restoreColumn((JSONObject) list.get(LIST_TYPE)); } arraySize = (Long) list.get(SIZE); if (list.containsKey(ENUM_OPTIONS)) { JSONArray optionsArray = (JSONArray) list.get(ENUM_OPTIONS); for (int n = 0; n < optionsArray.size(); n++) { options.add((String) optionsArray.get(n)); } } } ColumnType type = ColumnType.valueOf((String) obj.get(TYPE)); Column output = null; switch (type) { case ARRAY: output = new Array(name, listType).setSize(arraySize); break; case BINARY: Long charSize = (Long) obj.get(CHAR_SIZE); output = new Binary(name).setCharSize(charSize); break; case BIT: output = new Bit(name); break; case DATE: output = new Date(name); break; case DATE_TIME: Boolean hasFraction = (Boolean) obj.get(FRACTION); Boolean hasTimezone = (Boolean) obj.get(TIMEZONE); output = new DateTime(name, hasFraction, hasTimezone); break; case DECIMAL: Integer precision = obj.get(PRECISION) != null ? ((Long) obj.get(PRECISION)).intValue() : null; Integer scale = obj.get(SCALE) != null ? ((Long) obj.get(SCALE)).intValue() : null; output = new Decimal(name, precision, scale); break; case ENUM: output = new Enum(name, options); break; case FIXED_POINT: Boolean signed = (Boolean) obj.get(SIGNED); Long fixedPointByteSize = (Long) obj.get(BYTE_SIZE); output = new FixedPoint(name, fixedPointByteSize, signed); break; case FLOATING_POINT: Long floatingPointByteSize = (Long) obj.get(BYTE_SIZE); output = new FloatingPoint(name, floatingPointByteSize); break; case MAP: output = new Map(name, key, value); break; case SET: output = new Set(name, listType); break; case TEXT: charSize = (Long) obj.get(CHAR_SIZE); output = new Text(name).setCharSize(charSize); break; case TIME: Boolean hasTimeFraction = (Boolean) obj.get(FRACTION); output = new Time(name, hasTimeFraction); break; case UNKNOWN: Long jdbcType = (Long) obj.get(JDBC_TYPE); output = new Unknown(name).setJdbcType(jdbcType); break; default: // TODO(Jarcec): Throw an exception of unsupported type? } output.setNullable(nullable); return output; } private SchemaSerialization() { // Serialization is prohibited } }