001/*
002 * Copyright 2006-2007 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.batch.sample.domain.trade;
018
019import org.springframework.batch.core.StepExecution;
020import org.springframework.batch.core.listener.StepExecutionListenerSupport;
021import org.springframework.batch.item.file.transform.FieldSet;
022import org.springframework.batch.item.file.transform.LineTokenizer;
023
024/**
025 * Composite {@link LineTokenizer} that delegates the tokenization of a line to one of two potential
026 * tokenizers.  The file format in this case uses one character, either F, A, U, or D to indicate 
027 * whether or not the line is an a footer record, or a customer add, update, or delete, and 
028 * will delegate accordingly.
029 * 
030 * @author Lucas Ward
031 * @since 2.0
032 */
033public class CompositeCustomerUpdateLineTokenizer extends StepExecutionListenerSupport implements LineTokenizer {
034
035        private LineTokenizer customerTokenizer;
036        private LineTokenizer footerTokenizer;
037        private StepExecution stepExecution;
038        
039        /* (non-Javadoc)
040         * @see org.springframework.batch.item.file.transform.LineTokenizer#tokenize(java.lang.String)
041         */
042        @Override
043        public FieldSet tokenize(String line) {
044                
045                if(line.charAt(0) == 'F'){
046                        //line starts with F, so the footer tokenizer should tokenize it.
047                        FieldSet fs = footerTokenizer.tokenize(line);
048                        long customerUpdateTotal = stepExecution.getReadCount();
049                        long fileUpdateTotal = fs.readLong(1);
050                        if(customerUpdateTotal != fileUpdateTotal){
051                                throw new IllegalStateException("The total number of customer updates in the file footer does not match the " +
052                                                "number entered  File footer total: [" + fileUpdateTotal + "] Total encountered during processing: [" +
053                                                customerUpdateTotal + "]");
054                        }
055                        else{
056                                //return null, because the footer indicates an end of processing.
057                                return null;
058                        }
059                }
060                else if(line.charAt(0) == 'A' || line.charAt(0) == 'U' || line.charAt(0) == 'D'){
061                        //line starts with A,U, or D, so it must be a customer operation.
062                        return customerTokenizer.tokenize(line);
063                }
064                else{
065                        //If the line doesn't start with any of the characters above, it must obviously be invalid.
066                        throw new IllegalArgumentException("Invalid line encountered for tokenizing: " + line);
067                }
068        }
069        
070        @Override
071        public void beforeStep(StepExecution stepExecution) {
072                this.stepExecution = stepExecution;
073        }
074
075        /**
076         * Set the {@link LineTokenizer} that will be used to tokenize any lines that begin with
077         * A, U, or D, and are thus a customer operation.
078         *
079         * @param customerTokenizer tokenizer to delegate to for customer operation records
080         */
081        public void setCustomerTokenizer(LineTokenizer customerTokenizer) {
082                this.customerTokenizer = customerTokenizer;
083        }
084        
085        /**
086         * Set the {@link LineTokenizer} that will be used to tokenize any lines that being with
087         * F and is thus a footer record.
088         * 
089         * @param footerTokenizer tokenizer to delegate to for footer records
090         */
091        public void setFooterTokenizer(LineTokenizer footerTokenizer) {
092                this.footerTokenizer = footerTokenizer;
093        }
094}