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

com.univocity.parsers.common.fields.FieldConversionMapping Maven / Gradle / Ivy

Go to download

univocity's open source parsers for processing different text formats using a consistent API

There is a newer version: 2.9.1
Show newest version
/*******************************************************************************
 * Copyright 2014 uniVocity Software Pty Ltd
 *
 * 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.univocity.parsers.common.fields;

import java.util.*;

import com.univocity.parsers.common.*;
import com.univocity.parsers.conversions.*;

/**
 * A class for mapping field selections to sequences of {@link Conversion} objects
 *
 * @author uniVocity Software Pty Ltd - [email protected]
 *
 */
public class FieldConversionMapping {

	/**
	 * This list contains the sequence of conversions applied to sets of fields over multiple calls.
	 * 

It is shared by {@link FieldConversionMapping#fieldNameConversionMapping}, {@link FieldConversionMapping#fieldIndexConversionMapping} and {@link FieldConversionMapping#convertAllMapping}. *

Every time the user associates a sequence of conversions to a field, conversionSequence list will receive the FieldSelector. */ private List conversionSequence = new ArrayList(); private AbstractConversionMapping fieldNameConversionMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new FieldNameSelector(); } }; private AbstractConversionMapping fieldIndexConversionMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new FieldIndexSelector(); } }; private AbstractConversionMapping convertAllMapping = new AbstractConversionMapping(conversionSequence) { @Override protected FieldSelector newFieldSelector() { return new AllIndexesSelector(); } }; /** * This is the final sequence of conversions applied to each index in a record. It is populated when {@link FieldConversionMapping#prepareExecution(String[])} is invoked. */ private Map>> conversionsByIndex = Collections.emptyMap(); /** * Prepares the conversions registered in this object to be executed against a given sequence of fields * * @param values The field sequence that identifies how records will be organized. *

This is generally the sequence of headers in a record, but it might be just the first parsed row from a given input (as field selection by index is allowed). */ public void prepareExecution(String[] values) { if (fieldNameConversionMapping.isEmpty() && fieldIndexConversionMapping.isEmpty()) { return; } if (!conversionsByIndex.isEmpty()) { return; } //Note this property is shared across all conversion mappings. This is required so //the correct conversion sequence is registered for all fields. conversionsByIndex = new HashMap>>(); // adds the conversions in the sequence they were created. for (FieldSelector next : conversionSequence) { fieldNameConversionMapping.prepareExecution(next, conversionsByIndex, values); fieldIndexConversionMapping.prepareExecution(next, conversionsByIndex, values); convertAllMapping.prepareExecution(next, conversionsByIndex, values); } } public void applyConversionsOnAllFields(Conversion... conversions) { convertAllMapping.registerConversions(conversions); } public FieldSet applyConversionsOnFieldIndexes(Conversion... conversions) { return fieldIndexConversionMapping.registerConversions(conversions); } public FieldSet applyConversionsOnFieldNames(Conversion... conversions) { return fieldNameConversionMapping.registerConversions(conversions); } /** * Applies a sequence of conversions associated with an Object value at a given index in a record. * @param executeInReverseOrder flag to indicate whether or not the conversion sequence must be executed in reverse order * @param index The index of parsed value in a record * @param value The value in a record * @return the Object resulting from a sequence of conversions against the original value. */ @SuppressWarnings("unchecked") public Object reverseConversions(boolean executeInReverseOrder, int index, Object value) { List> conversions = conversionsByIndex.get(index); if (conversions != null) { Conversion conversion = null; try { if (executeInReverseOrder) { for (int i = conversions.size() - 1; i >= 0; i--) { conversion = conversions.get(i); value = ((Conversion) conversion).revert(value); } } else { for (Conversion c : conversions) { conversion = c; value = ((Conversion) conversion).revert(value); } } } catch (Exception ex) { if (conversion != null) { throw new IllegalStateException("Error converting value '" + value + "' using conversion " + conversion.getClass().getName(), ex); } else { throw new IllegalStateException("Error converting value '" + value + "'", ex); } } } return value; } /** * Applies a sequence of conversions associated with a String value parsed from a given index. * @param index The index of parsed value in a record * @param stringValue The parsed value in a record * @return the Object produced by a sequence of conversions against the original String value. */ public Object applyConversions(int index, String stringValue) { List> conversions = conversionsByIndex.get(index); if (conversions != null) { for (Conversion conversion : conversions) { Object result = null; try { result = conversion.execute(stringValue); if (result == null || result instanceof String) { stringValue = (String) result; } else { return result; } } catch (Exception ex) { throw new IllegalStateException("Error converting value '" + result + "' using conversion " + conversion.getClass().getName(), ex); } } } return stringValue; } } /** * Class responsible for managing field selections and any conversion sequence associated with each. * * @author uniVocity Software Pty Ltd - [email protected] * * @param the FieldSelector type information used to uniquely identify a field (e.g. references to field indexes would use Integer, while references to field names would use String). * * @see FieldNameSelector * @see FieldIndexSelector */ abstract class AbstractConversionMapping { private Map[]> conversionsMap; private final List conversionSequence; AbstractConversionMapping(List conversionSequence) { this.conversionSequence = conversionSequence; } /** * Registers a sequence of conversions to a set of fields. *

The selector instance that is used to store which fields should be converted is added to the {@link AbstractConversionMapping#conversionSequence} list in order to keep track of the correct conversion order. *

This is required further conversion sequences might be added to the same fields in separate calls. * * @param conversions the conversion sequence to be applied to a set of fields. * @return a FieldSet which provides methods to select the fields that must be converted or null if the FieldSelector returned by #newFieldSelector is not an instance of FieldSet (which is the case of {@link AllIndexesSelector}). * */ @SuppressWarnings("unchecked") public FieldSet registerConversions(Conversion... conversions) { ArgumentUtils.noNulls("Conversions", conversions); FieldSelector selector = newFieldSelector(); if (conversionsMap == null) { conversionsMap = new LinkedHashMap[]>(); } conversionsMap.put(selector, conversions); conversionSequence.add(selector); if (selector instanceof FieldSet) { return (FieldSet) selector; } return null; } /** * Creates a FieldSelector instance of the desired type. Used in @link FieldConversionMapping}. * @return a new FieldSelector instance. */ protected abstract FieldSelector newFieldSelector(); /** * Get all indexes in the given selector and adds the conversions defined at that index to the map of conversionsByIndex. *

This method is called in the same sequence each selector was created (in {@link FieldConversionMapping#prepareExecution(String[])}) *

At the end of the process, the map of conversionsByIndex will have each index with its list of conversions in the order they were declared. * * @param selector the selected fields for a given conversion sequence. * @param conversionsByIndex map of all conversions registered to every field index, in the order they were declared * @param values The field sequence that identifies how records will be organized. *

This is generally the sequence of headers in a record, but it might be just the first parsed row from a given input (as field selection by index is allowed). */ public void prepareExecution(FieldSelector selector, Map>> conversionsByIndex, String[] values) { if (conversionsMap == null) { return; } //conversionsMap contains maps the conversions applied to a field selection //we will match the indexes where these conversions where applied and add them to the corresponding list in conversionsByIndex Conversion[] conversions = conversionsMap.get(selector); if (conversions == null) { return; } int[] fieldIndexes = selector.getFieldIndexes(values); for (int fieldIndex : fieldIndexes) { List> conversionsAtIndex = conversionsByIndex.get(fieldIndex); if (conversionsAtIndex == null) { conversionsAtIndex = new ArrayList>(); conversionsByIndex.put(fieldIndex, conversionsAtIndex); } validateDuplicates(selector, conversionsAtIndex, conversions); conversionsAtIndex.addAll(Arrays.asList(conversions)); } } /** * Ensures an individual field does not have the same conversion object applied to it more than once. * @param selector the selection of fields * @param conversionsAtIndex the sequence of conversions applied to a given index * @param conversionsToAdd the sequence of conversions to add to conversionsAtIndex */ private void validateDuplicates(FieldSelector selector, List> conversionsAtIndex, Conversion[] conversionsToAdd) { for (Conversion toAdd : conversionsToAdd) { for (Conversion existing : conversionsAtIndex) { if (toAdd == existing) { throw new IllegalArgumentException("Duplicate conversion " + toAdd.getClass().getName() + " being applied to " + selector.describe()); } } } } /** * Queries if any conversions were associated with any field * @return true if no conversions were associated with any field; false otherwise */ public boolean isEmpty() { return conversionsMap == null || conversionsMap.isEmpty(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy