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

com.bazaarvoice.jolt.chainr.spec.ChainrEntry Maven / Gradle / Ivy

/*
 * Copyright 2013 Bazaarvoice, 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.bazaarvoice.jolt.chainr.spec;

import com.bazaarvoice.jolt.CardinalityTransform;
import com.bazaarvoice.jolt.Chainr;
import com.bazaarvoice.jolt.Defaultr;
import com.bazaarvoice.jolt.JoltTransform;
import com.bazaarvoice.jolt.Removr;
import com.bazaarvoice.jolt.Shiftr;
import com.bazaarvoice.jolt.Sortr;
import com.bazaarvoice.jolt.SpecDriven;
import com.bazaarvoice.jolt.exception.SpecException;
import com.bazaarvoice.jolt.utils.StringTools;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Helper class that encapsulates the information one of the individual transform entries in
 * the Chainr spec's list.
 */
public class ChainrEntry {

    /**
     * Map transform "operation" names to the classes that handle them
     */
    public static final Map STOCK_TRANSFORMS;
    static {
        HashMap temp = new HashMap<>();
        temp.put( "shift", Shiftr.class.getCanonicalName() );
        temp.put( "default", Defaultr.class.getCanonicalName() );
        temp.put( "remove", Removr.class.getCanonicalName() );
        temp.put( "sort", Sortr.class.getCanonicalName() );
        temp.put( "cardinality", CardinalityTransform.class.getCanonicalName() );
        STOCK_TRANSFORMS = Collections.unmodifiableMap( temp );
    }

    public static final String OPERATION_KEY = "operation";
    public static final String SPEC_KEY = "spec";

    private final int index;
    private final Object spec;
    private final String operationClassName;

    private final Class joltTransformClass;
    private final boolean isSpecDriven;

    /**
     * Process an element from the Chainr Spec into a ChainrEntry class.
     * This method tries to validate the syntax of the Chainr spec, whereas
     * the ChainrInstantiator deals with loading the Transform classes.
     *
     * @param chainrEntryObj the unknown Object from the Chainr list
     * @param index the index of the chainrEntryObj, used in reporting errors
     */
    public ChainrEntry( int index, Object chainrEntryObj ) {

        if ( ! (chainrEntryObj instanceof Map ) ) {
            throw new SpecException( "JOLT ChainrEntry expects a JSON map - Malformed spec" + getErrorMessageIndexSuffix() );
        }

        @SuppressWarnings( "unchecked" ) // We know it is a Map due to the check above
        Map chainrEntryMap = (Map) chainrEntryObj;

        this.index = index;

        String opString = extractOperationString( chainrEntryMap );

        if ( opString == null ) {
            throw new SpecException( "JOLT Chainr 'operation' must implement Transform or ContextualTransform" + getErrorMessageIndexSuffix() );
        }

        if ( STOCK_TRANSFORMS.containsKey( opString ) ) {
            operationClassName = STOCK_TRANSFORMS.get( opString );
        }
        else {
            operationClassName = opString;
        }

        joltTransformClass = loadJoltTransformClass();

        spec = chainrEntryMap.get( ChainrEntry.SPEC_KEY );

        isSpecDriven = SpecDriven.class.isAssignableFrom( joltTransformClass );
        if ( isSpecDriven && ! chainrEntryMap.containsKey( SPEC_KEY ) ) {
            throw new SpecException( "JOLT Chainr - Transform className:" + joltTransformClass.getCanonicalName() + " requires a spec" + getErrorMessageIndexSuffix() );
        }
    }

    private String extractOperationString( Map chainrEntryMap ) {

        Object operationNameObj = chainrEntryMap.get( ChainrEntry.OPERATION_KEY );
        if ( operationNameObj == null ) {
            return null;
        }
        else if ( operationNameObj instanceof String) {
            if ( StringTools.isBlank((String) operationNameObj) ) {
                throw new SpecException( "JOLT Chainr '" + ChainrEntry.OPERATION_KEY + "' should not be blank" + getErrorMessageIndexSuffix() );
            }
            return (String) operationNameObj;
        }
        else {
            throw new SpecException( "JOLT Chainr needs a '" + ChainrEntry.OPERATION_KEY + "' of type String" + getErrorMessageIndexSuffix() );
        }
    }

    private Class loadJoltTransformClass() {

        try {
            Class opClass = Class.forName( operationClassName );

            if ( Chainr.class.isAssignableFrom( opClass ) ) {
                throw new SpecException( "Attempt to nest Chainr inside itself" + getErrorMessageIndexSuffix() );
            }

            if ( ! JoltTransform.class.isAssignableFrom( opClass ) )
            {
                throw new SpecException( "JOLT Chainr class:" + operationClassName + " does not implement the JoltTransform interface" + getErrorMessageIndexSuffix() );
            }

            @SuppressWarnings( "unchecked" ) // We know it is some type of Transform due to the check above
            Class transformClass = (Class) opClass;

            return transformClass;

        } catch ( ClassNotFoundException e ) {
            throw new SpecException( "JOLT Chainr could not find transform class:" + operationClassName + getErrorMessageIndexSuffix(), e );
        }
    }


    /**
     * Generate an error message suffix what lists the index of the ChainrEntry in the overall ChainrSpec.
     */
    public String getErrorMessageIndexSuffix() {
        return " at index:" + index + ".";
    }

    /**
     * @return Spec for the transform, can be null
     */
    public Object getSpec() {
        return spec;
    }

    /**
     * @return Class instance specified by this ChainrEntry
     */
    public Class getJoltTransformClass() {
        return joltTransformClass;
    }

    /**
     * @return true if the Jolt Transform specified by this ChainrEntry implements the SpecTransform interface
     */
    public boolean isSpecDriven() {
        return isSpecDriven;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy