All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.hollow.jsonadapter.discover.HollowJsonAdapterSchemaDiscoverer Maven / Gradle / Ivy

There is a newer version: 7.13.0
Show newest version
/*
 *  Copyright 2016-2019 Netflix, Inc.
 *
 *     Licensed 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 com.netflix.hollow.jsonadapter.discover;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.netflix.hollow.core.schema.HollowObjectSchema.FieldType;
import com.netflix.hollow.jsonadapter.AbstractHollowJsonAdaptorTask;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class HollowJsonAdapterSchemaDiscoverer extends AbstractHollowJsonAdaptorTask {

    private final Set mapTypes;
    private final Map discoveredSchemas;
    private final HollowSchemaNamer schemaNamer;

    public HollowJsonAdapterSchemaDiscoverer(String typeName) {
        super(typeName, "scan");
        this.mapTypes = new HashSet();
        this.discoveredSchemas = new ConcurrentHashMap();
        this.schemaNamer = new HollowSchemaNamer();
    }

    public void addMapTypes(String... types) {
        for(String type : types) {
            String mapType = schemaNamer.schemaNameFromPropertyPath(type);
            mapTypes.add(mapType);
        }
    }

    public void addMapTypes(Set types) {
        for (String type : types) {
            String mapType = schemaNamer.schemaNameFromPropertyPath(type);
            mapTypes.add(mapType);
        }
    }

    @Override
    protected int processRecord(JsonParser parser) throws IOException {
        if (isDebug) System.out.println("\nProcessRecord: " + typeName);
        final HollowDiscoveredSchema schema = discoveredSchema(typeName, DiscoveredSchemaType.OBJECT, null);
        parser.nextToken();
        discoverSchemas(parser, schema);
        return -1;
    }

    public Collection discoverSchemas(File jsonFile, Integer maxSample) throws Exception {
        processFile(jsonFile, maxSample);
        return discoveredSchemas.values();
    }

    public Collection discoverSchemas(Reader jsonReader, Integer maxSample) throws Exception {
        processFile(jsonReader, maxSample);
        return discoveredSchemas.values();
    }

    private void discoverSchemas(JsonParser parser, HollowDiscoveredSchema schema) throws IOException {
        JsonToken token = parser.nextToken();

        while (token != JsonToken.END_OBJECT) {
            String fieldName = parser.getCurrentName();
            //if (isDebug) parser = print("discoverSchemas fieldName=" + fieldName, token, parser);

            discoverSchemaField(parser, token, fieldName, schema);
            token = parser.nextToken();
        }
    }

    private void discoverSchemaField(JsonParser parser, JsonToken token, String fieldName, HollowDiscoveredSchema schema) throws IOException {

        if(token != JsonToken.FIELD_NAME) {
            switch(token) {
                case START_ARRAY:
                    String listName = schemaNamer.subCollectionName(schema.schemaName, "ArrayOf", fieldName);
                    String elementName = schemaNamer.subObjectName(schema.schemaName, "", fieldName);
                    if (isDebug) System.out.println(String.format("\t ARR[START] token=%s schemaName=%s fieldName=%s listName=%s elementName=%s", token, schema.schemaName, fieldName, listName, elementName));

                    discoveredSchema(listName, DiscoveredSchemaType.LIST, elementName);
                    schema.addField(fieldName, FieldType.REFERENCE, listName);

                    HollowDiscoveredSchema elementSchema = discoveredSchema(elementName, DiscoveredSchemaType.OBJECT, null);
                    discoverSubArraySchemas(parser, elementSchema);
                    if (isDebug) System.out.println(String.format("\t ARR[END] token=%s schemaName=%s fieldName=%s listName=%s elementName=%s elementSchema=%s", token, schema.schemaName, fieldName, listName, elementName, elementSchema));

                    break;
                case START_OBJECT:
                    String subObjectName = schemaNamer.subObjectName(schema.schemaName, "", fieldName);
                    //if (isDebug) System.out.println("\t\t [MAP CHECK] subObjectName=" + subObjectName + "\t" + mapTypes.contains(subObjectName) + "\t" + mapTypes);
                    if(mapTypes.contains(subObjectName)) {
                        String subMapName = schemaNamer.subCollectionName(schema.schemaName, "MapOf", fieldName);
                        if (isDebug) System.out.println(String.format("\t MAP[START] token=%s schemaName=%s fieldName=%s subMapName=%s subObjectName=%s", token, schema.schemaName, fieldName, subMapName, subObjectName));

                        discoveredSchema(subMapName, DiscoveredSchemaType.MAP, subObjectName);
                        schema.addField(fieldName, FieldType.REFERENCE, subMapName);

                        HollowDiscoveredSchema valueSchema = discoveredSchema(subObjectName, DiscoveredSchemaType.OBJECT, null);
                        discoverSubMapSchemas(parser, valueSchema);
                        if (isDebug) System.out.println(String.format("\t MAP[END] token=%s schemaName=%s fieldName=%s subMapName=%s subObjectName=%s valueSchema=%s", token, schema.schemaName, fieldName, subMapName, subObjectName, valueSchema));
                    } else {
                        if (isDebug) System.out.println(String.format("\t OBJ[START] token=%s schemaName=%s fieldName=%s subObjectName=%s", token, schema.schemaName, fieldName, subObjectName));
                        HollowDiscoveredSchema subObjectSchema = discoveredSchema(subObjectName, DiscoveredSchemaType.OBJECT, null);
                        if (fieldName != null) schema.addField(fieldName, FieldType.REFERENCE, subObjectName);

                        discoverSchemas(parser, subObjectSchema);
                        if (isDebug) System.out.println(String.format("\t OBJ[END] token=%s schemaName=%s fieldName=%s subObjectName=%s subObjectSchema=%s", token, schema.schemaName, fieldName, subObjectName, subObjectSchema));
                    }

                    break;
                case VALUE_NUMBER_INT:
                    if (isDebug) System.out.println(String.format("\t FIELD token=%s schemaName=%s fieldName=%s value=%s", token, schema.schemaName, fieldName, parser.getLongValue()));
                    schema.addField(fieldName, FieldType.LONG);
                    break;
                case VALUE_NUMBER_FLOAT:
                    if (isDebug) System.out.println(String.format("\t FIELD token=%s schemaName=%s fieldName=%s value=%s", token, schema.schemaName, fieldName, parser.getDoubleValue()));
                    schema.addField(fieldName, FieldType.DOUBLE);
                    break;
                case VALUE_NULL:
                    if (isDebug) System.out.println(String.format("\t FIELD token=%s schemaName=%s fieldName=%s", token, schema.schemaName, fieldName));
                    break;
                case VALUE_STRING:
                    if (isDebug) System.out.println(String.format("\t FIELD token=%s schemaName=%s fieldName=%s value=%s", token, schema.schemaName, fieldName, parser.getValueAsString()));
                    schema.addField(fieldName, FieldType.STRING);
                    break;
                case VALUE_FALSE:
                case VALUE_TRUE:
                    if (isDebug) System.out.println(String.format("\t FIELD token=%s schemaName=%s fieldName=%s value=%s", token, schema.schemaName, fieldName, parser.getBooleanValue()));
                    schema.addField(fieldName, FieldType.BOOLEAN);
                    break;
                default:
            }
        }
    }

    private void discoverSubArraySchemas(JsonParser parser, HollowDiscoveredSchema objectSchema) throws IOException {
        JsonToken token = parser.nextToken();

        while(token != JsonToken.END_ARRAY) {
            if(token == JsonToken.START_OBJECT) {
                discoverSchemas(parser, objectSchema);
            } else {
                discoverSchemaField(parser, token, "value", objectSchema);
            }
            token = parser.nextToken();
        }
    }

    private void discoverSubMapSchemas(JsonParser parser, HollowDiscoveredSchema objectSchema) throws IOException {
        JsonToken token = parser.nextToken();
        if (isDebug) System.out.println("discoverSubMapSchemas[START]: token=" + token + ", fieldname=" + parser.getCurrentName());

        while(token != JsonToken.END_OBJECT) {
            if (isDebug) System.out.println("discoverSubMapSchemas[LOOP]: token=" + token + ", fieldname=" + parser.getCurrentName());
            if(token != JsonToken.FIELD_NAME) {
                if(token == JsonToken.START_OBJECT) {
                    if (isDebug) System.out.println("discoverSubMapSchemas[LOOP] discoverSchemas: token=" + token + ", fieldname=" + parser.getCurrentName());
                    discoverSchemas(parser, objectSchema);
                } else {
                    if (isDebug) System.out.println("discoverSubMapSchemas[LOOP] discoverSchemaField: token=" + token + ", fieldname=" + parser.getCurrentName());
                    discoverSchemaField(parser, token, "value", objectSchema);
                }
            }
            token = parser.nextToken();
        }

        if (isDebug) System.out.println("discoverSubMapSchemas[END]: token=" + token);
    }

    private HollowDiscoveredSchema discoveredSchema(String schemaName, DiscoveredSchemaType type, String listSubType) {
        HollowDiscoveredSchema schema = discoveredSchemas.get(schemaName);
        if(schema == null) {
            synchronized (discoveredSchemas) {
                schema = discoveredSchemas.get(schemaName);
                if (schema == null) {
                    schema = new HollowDiscoveredSchema(schemaName, type, listSubType);
                    discoveredSchemas.put(schemaName, schema);
                }
            }
        }

        if(schema.type != type)
            throw new RuntimeException(schemaName + ": Expected schema of type " + type + " but was " + schema.type);

        return schema;
    }

    private static String padRight(String s, int n) {
        return String.format("%1$-" + n + "s", s);
    }

    public static void analyzeSchemas(Collection schemas, int largeNumOfFieldsThreshold) {
        for (HollowDiscoveredSchema schema : schemas) {
            boolean isObjectWithLargeNumOfFieldsFields = (schema.type == DiscoveredSchemaType.OBJECT) && (schema.fields.size() > largeNumOfFieldsThreshold);
            System.out.print("\t");
            System.out.println(schema);

            // Fields
            int fieldCount = schema.fields.size();
            if (fieldCount == 0) continue;

            int i = 0;
            int maxKeyLen = 0;
            String fieldHeaderTemplate = isObjectWithLargeNumOfFieldsFields ? "[***] Object with lots of fields: %s" : "Field Count: %s";
            StringBuilder builder = new StringBuilder("\t  - ");
            builder.append(String.format(fieldHeaderTemplate, fieldCount)).append("\n");
            for (String key : schema.fields.keySet()) {
                if (key.length() > maxKeyLen) maxKeyLen = key.length();
            } // find max key len
            for (Map.Entry entry : schema.fields.entrySet()) {
                builder.append("\t\t").append(++i).append(": fieldname=").append(padRight(entry.getKey(), maxKeyLen)).append("\t -> ").append(entry.getValue()).append("\n");
            }
            System.out.print(builder);

        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy