com.fasterxml.jackson.dataformat.csv.CsvMapper.java Source code

Java tutorial

Introduction

Here is the source code for com.fasterxml.jackson.dataformat.csv.CsvMapper.java

Source

package com.fasterxml.jackson.dataformat.csv;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.csv.impl.LRUMap;

/**
 * Specialized {@link ObjectMapper}, with extended functionality to
 * produce {@link CsvSchema} instances out of POJOs.
 */
public class CsvMapper extends ObjectMapper {
    private static final long serialVersionUID = -4573529235914611523L;

    /**
     * Simple caching for schema instances, given that they are relatively expensive
     * to construct; this one is for "loose" (non-typed) schemas
     */
    protected final LRUMap<JavaType, CsvSchema> _untypedSchemas = new LRUMap<JavaType, CsvSchema>(8, 32);

    /**
     * Simple caching for schema instances, given that they are relatively expensive
     * to construct; this one is for typed schemas
     */
    protected final LRUMap<JavaType, CsvSchema> _typedSchemas = new LRUMap<JavaType, CsvSchema>(8, 32);

    /*
    /**********************************************************************
    /* Life-cycle
    /**********************************************************************
     */

    public CsvMapper() {
        this(new CsvFactory());
    }

    public CsvMapper(CsvFactory f) {
        super(f);
        // As per #11: default to alphabetic ordering
        enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
    }

    /*
    /**********************************************************************
    /* Configuration
    /**********************************************************************
     */

    public CsvMapper configure(CsvGenerator.Feature f, boolean state) {
        return state ? enable(f) : disable(f);
    }

    public CsvMapper configure(CsvParser.Feature f, boolean state) {
        return state ? enable(f) : disable(f);
    }

    public CsvMapper enable(CsvGenerator.Feature f) {
        ((CsvFactory) _jsonFactory).enable(f);
        return this;
    }

    public CsvMapper enable(CsvParser.Feature f) {
        ((CsvFactory) _jsonFactory).enable(f);
        return this;
    }

    public CsvMapper disable(CsvGenerator.Feature f) {
        ((CsvFactory) _jsonFactory).disable(f);
        return this;
    }

    public CsvMapper disable(CsvParser.Feature f) {
        ((CsvFactory) _jsonFactory).disable(f);
        return this;
    }

    /*
    /**********************************************************************
    /* Additional typed accessors
    /**********************************************************************
     */

    /**
     * Overridden with more specific type, since factory we have
     * is always of type {@link CsvFactory}
     */
    @Override
    public final CsvFactory getFactory() {
        return (CsvFactory) _jsonFactory;
    }

    /*
    /**********************************************************************
    /* CsvSchema construction; overrides, new methods
    /**********************************************************************
     */

    /**
     * Method that can be used to determine a CSV schema to use for given
     * POJO type, using default serialization settings including ordering.
     * Definition will not be strictly typed (that is, all columns are
     * just defined to be exposed as String tokens).
     */
    public CsvSchema schemaFor(JavaType pojoType) {
        return _schemaFor(pojoType, _untypedSchemas, false);
    }

    public final CsvSchema schemaFor(Class<?> pojoType) {
        return _schemaFor(constructType(pojoType), _untypedSchemas, false);
    }

    public final CsvSchema schemaFor(TypeReference<?> pojoTypeRef) {
        return _schemaFor(constructType(pojoTypeRef.getType()), _untypedSchemas, false);
    }

    /**
     * Method that can be used to determine a CSV schema to use for given
     * POJO type, using default serialization settings including ordering.
     * Definition WILL be strictly typed: that is, code will try to 
     * determine type limitations which may make parsing more efficient
     * (especially for numeric types like java.lang.Integer).
     */
    public CsvSchema typedSchemaFor(JavaType pojoType) {
        return _schemaFor(pojoType, _typedSchemas, true);
    }

    public final CsvSchema typedSchemaFor(Class<?> pojoType) {
        return _schemaFor(constructType(pojoType), _typedSchemas, true);
    }

    public final CsvSchema typedSchemaFor(TypeReference<?> pojoTypeRef) {
        return _schemaFor(constructType(pojoTypeRef.getType()), _typedSchemas, true);
    }

    /**
     * Convenience method which is functionally equivalent to:
     *<pre>
     *  reader(pojoType).withSchema(schemaFor(pojoType));
     *</pre>
     * that is, constructs a {@link ObjectReader} which both binds to
     * specified type and uses "loose" {@link CsvSchema} introspected from
     * specified type (one without strict inferred typing).
     *<p>
     * @param pojoType Type used both for data-binding (result type) and for
     *   schema introspection. NOTE: must NOT be an array or Collection type, since
     *   these only make sense for data-binding (like arrays of objects to bind),
     *   but not for schema construction (no CSV types can be mapped to arrays
     *   or Collections)
     */
    public ObjectReader readerWithSchemaFor(Class<?> pojoType) {
        JavaType type = constructType(pojoType);
        /* sanity check: not useful for structured types, since
         * schema type will need to differ from data-bind type
         */
        if (type.isArrayType() || type.isCollectionLikeType()) {
            throw new IllegalArgumentException("Type can NOT be a Collection or array type");
        }
        return reader(type).with(schemaFor(type));
    }

    /**
     * Convenience method which is functionally equivalent to:
     *<pre>
     *  reader(pojoType).withSchema(typedSchemaFor(pojoType));
     *</pre>
     * that is, constructs a {@link ObjectReader} which both binds to
     * specified type and uses "strict" {@link CsvSchema} introspected from
     * specified type (one where typing is inferred).
     */
    public ObjectReader readerWithTypedSchemaFor(Class<?> pojoType) {
        JavaType type = constructType(pojoType);
        /* sanity check: not useful for structured types, since
         * schema type will need to differ from data-bind type
         */
        if (type.isArrayType() || type.isCollectionLikeType()) {
            throw new IllegalArgumentException("Type can NOT be a Collection or array type");
        }
        return reader(type).with(typedSchemaFor(type));
    }

    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */

    protected CsvSchema _schemaFor(JavaType pojoType, LRUMap<JavaType, CsvSchema> schemas, boolean typed) {
        synchronized (schemas) {
            CsvSchema s = schemas.get(pojoType);
            if (s != null) {
                return s;
            }
        }
        final AnnotationIntrospector intr = _deserializationConfig.getAnnotationIntrospector();
        CsvSchema.Builder builder = CsvSchema.builder();
        _addSchemaProperties(builder, intr, typed, pojoType, null);
        CsvSchema result = builder.build();
        synchronized (schemas) {
            schemas.put(pojoType, result);
        }
        return result;
    }

    protected void _addSchemaProperties(CsvSchema.Builder builder, AnnotationIntrospector intr, boolean typed,
            JavaType pojoType, NameTransformer unwrapper) {
        BeanDescription beanDesc = getSerializationConfig().introspect(pojoType);
        for (BeanPropertyDefinition prop : beanDesc.findProperties()) {
            // ignore setter-only properties:
            if (!prop.couldSerialize()) {
                continue;
            }
            // [Issue#15]: handle unwrapped props
            AnnotatedMember m = prop.getPrimaryMember();
            if (m != null) {
                NameTransformer nextUnwrapper = intr.findUnwrappingNameTransformer(prop.getPrimaryMember());
                if (nextUnwrapper != null) {
                    if (unwrapper != null) {
                        nextUnwrapper = NameTransformer.chainedTransformer(unwrapper, nextUnwrapper);
                    }
                    JavaType nextType = m.getType(beanDesc.bindingsForBeanType());
                    _addSchemaProperties(builder, intr, typed, nextType, nextUnwrapper);
                    continue;
                }
            }
            // Then name wrapping/unwrapping
            String name = prop.getName();
            if (unwrapper != null) {
                name = unwrapper.transform(name);
            }
            if (typed && m != null) {
                builder.addColumn(name, _determineType(m.getRawType()));
            } else {
                builder.addColumn(name);
            }
        }
    }

    // should not be null since couldSerialize() returned true, so:
    protected CsvSchema.ColumnType _determineType(Class<?> propType) {
        // First let's check certain cases that ought to be just presented as Strings...
        if (propType == String.class || propType == Character.TYPE || propType == Character.class) {
            return CsvSchema.ColumnType.STRING;
        }
        // all primitive types are good, since "numeric" allows boolean
        if (propType.isPrimitive() || Number.class.isAssignableFrom(propType)) {
            return CsvSchema.ColumnType.NUMBER;
        }
        // but in general we will just do what we can:
        return CsvSchema.ColumnType.NUMBER_OR_STRING;
    }
}