fr.acxio.tools.agia.item.MultiLineItemReader.java Source code

Java tutorial

Introduction

Here is the source code for fr.acxio.tools.agia.item.MultiLineItemReader.java

Source

package fr.acxio.tools.agia.item;

/*
 * Copyright 2014 Acxio
 * 
 * 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.
 */

import java.util.ArrayList;
import java.util.List;

import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.util.Assert;

import fr.acxio.tools.agia.expression.support.AbstractExpressionEvaluator;

/**
 * <p>
 * Item reader for records made of many lines with different mappings and having
 * non-explicit rules for detecting the ends of the records.
 * </p>
 * <p>
 * This item reader delegates the real reading, parsing and mapping to its
 * {@code delegate}.
 * </p>
 * <p>
 * The {@code newRecordCondition} is a boolean expression that can use the
 * {@code currentVariableName} and the {@code nextVariableName}, respectively
 * "current" and "next" by default.
 * </p>
 * <p>
 * As their names are self-explanatory, these variables contain the current line
 * and the next line from the stream.
 * </p>
 * <p>
 * The delegate reader may return different FieldSets, having different fields
 * from one another. The condition must take this into account.
 * </p>
 * <p>
 * Every time the end of a record is reach in the current line, the condition
 * should evaluate to {@code true} and a list of
 * {@link org.springframework.batch.item.file.transform.FieldSet FieldSet} is
 * returned.
 * </p>
 * <p>
 * The returned FieldSets by the delegate reader may not have the same fields.
 * </p>
 * <p>Subclasses must implement {@code mapFieldSets} to transform the list
 * of read FieldSet into items.</p>
 * 
 * @param <T> The type of the returned items
 * 
 * @author pcollardez
 *
 */
public abstract class MultiLineItemReader<T> extends AbstractExpressionEvaluator implements ItemStreamReader<T> {

    private ItemReader<FieldSet> delegate;
    private FieldSet nextItem = null;
    private boolean delegateExhausted = false;

    private String newRecordCondition;

    private String currentVariableName = "current";
    private String nextVariableName = "next";

    public synchronized void setDelegate(ItemReader<FieldSet> sDelegate) {
        delegate = sDelegate;
    }

    public synchronized void setCurrentVariableName(String sCurrentVariableName) {
        Assert.hasText(sCurrentVariableName, "currentVariableName must not be empty");
        currentVariableName = sCurrentVariableName;
    }

    public synchronized void setNextVariableName(String sNextVariableName) {
        Assert.hasText(sNextVariableName, "nextVariableName must not be empty");
        nextVariableName = sNextVariableName;
    }

    public synchronized void setNewRecordCondition(String sNewRecordCondition) {
        newRecordCondition = sNewRecordCondition;
    }

    @Override
    public synchronized T read() {

        List<FieldSet> aTmpResult = new ArrayList<FieldSet>();
        boolean aConditionResult = false;

        FieldSet line = readNextFieldSet();
        while (!aConditionResult && (line != null)) {
            aTmpResult.add(line);
            if (nextItem != null) {
                updateContext(currentVariableName, (line.hasNames()) ? line.getProperties() : line.getValues(),
                        getEvaluationContext());
                updateContext(nextVariableName,
                        (nextItem.hasNames()) ? nextItem.getProperties() : nextItem.getValues(),
                        getEvaluationContext());
                aConditionResult = getExpressionResolver().evaluate(newRecordCondition, getEvaluationContext(),
                        Boolean.class);
            }
            if (!aConditionResult) {
                line = readNextFieldSet();
            }
        }

        return (aTmpResult.isEmpty() ? null : mapFieldSets(aTmpResult));
    }

    public abstract T mapFieldSets(List<FieldSet> sFieldSets);

    private FieldSet readNextFieldSet() {
        FieldSet returnItem = null;

        try {
            if (!delegateExhausted) {
                if (nextItem != null) {
                    returnItem = nextItem;
                    nextItem = null;
                } else {
                    returnItem = delegate.read();
                }

                nextItem = delegate.read();
                if (nextItem == null) {
                    delegateExhausted = true;
                }
            }
        } catch (Exception e) {
            throw new ItemReaderReadException("Cannot read next FieldSet", e);
        }

        return returnItem;
    }

    @Override
    public synchronized void open(ExecutionContext sExecutionContext) {
        delegateExhausted = false;
        ((ItemStream) delegate).open(sExecutionContext);
    }

    @Override
    public synchronized void update(ExecutionContext sExecutionContext) {
        ((ItemStream) delegate).update(sExecutionContext);
    }

    @Override
    public synchronized void close() {
        delegateExhausted = false;
        ((ItemStream) delegate).close();
    }
}