001/*
002 * Copyright 2011 The Kuali Foundation.
003 * 
004 * Licensed under the Educational Community License, Version 1.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 * http://www.opensource.org/licenses/ecl1.php
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 */
016package org.kuali.ole.sys.batch;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023import org.apache.commons.lang.StringUtils;
024
025/**
026 * The abstract parent of flat file specifications which use regexes to determine what line is parsed into what object
027 */
028public abstract class AbstractRegexSpecificationBase extends AbstractFlatFileSpecificationBase {
029    protected List<String> insignificantRegexPatterns;
030    protected List<Pattern> insignificantPatterns;
031    protected boolean trimLineBeforeMatch;
032    protected boolean fullMatch = true;
033
034    /**
035     * Matches the given line with an object to parse into, or null if no object could be found
036     * @see org.kuali.ole.sys.batch.FlatFileSpecification#determineClassForLine(java.lang.String)
037     */
038    public Class<?> determineClassForLine(String line) {
039        final String matchLine = trimLine(line);
040        for (FlatFileObjectSpecification objectSpecification : getObjectSpecifications()) {
041            final FlatFileRegexObjectSpecification regexObjectSpecification = (FlatFileRegexObjectSpecification)objectSpecification;
042            final Pattern pattern = regexObjectSpecification.getPattern();
043            if (matches(pattern, matchLine)) {
044                return regexObjectSpecification.getBusinessObjectClass();
045            }
046        }
047        for (Pattern insignificantPattern : getInsignificantPatterns()) {
048            if (matches(insignificantPattern, matchLine)) return null;
049        }
050        return defaultBusinessObjectClass;
051    }
052    
053    /**
054     * Trims the trailing space only from the given line, though only if trimLineBeforeMatch is true
055     * @param line the line to perhaps trim trailing spaces from
056     * @return the maybe trimmed line
057     */
058    protected String trimLine(String line) {
059        if (isTrimLineBeforeMatch()) {
060            return StringUtils.stripEnd(line, " \t\n\f\r");
061        }
062        return line;
063    }
064
065    /**
066     * Sets the insignificant regex patterns
067     * @param insignificantRegexPatterns the regex patterns for lines to ignore
068     */
069    public void setInsignificantRegexPatterns(List<String> insignificantRegexPatterns) {
070        this.insignificantRegexPatterns = insignificantRegexPatterns;
071        
072        this.insignificantPatterns = new ArrayList<Pattern>();
073        for (String regexPattern : insignificantRegexPatterns) {
074            final Pattern pattern = Pattern.compile(regexPattern);
075            insignificantPatterns.add(pattern);
076        }
077    }
078    
079    /**
080     * Determines if the given line matches the given pattern, following the full match rule
081     * @param pattern the pattern to match against
082     * @param line the parsed line to match
083     * @return true if the line matches and the line should be parsed by this object specification; false if this line should be given to the next object specification
084     */
085    protected boolean matches(Pattern pattern, String line) {
086        Matcher matcher = pattern.matcher(line);
087        if (fullMatch) {
088            return matcher.matches();
089        } else {
090            return matcher.find();
091        }
092    }
093
094    /**
095     * @return the List of compiled insignificant patterns
096     */
097    public List<Pattern> getInsignificantPatterns() {
098        return this.insignificantPatterns;
099    }
100
101    /**
102     * @return whether a parsed line will have trailing spaces removed (and trailing spaces only!)
103     */
104    public boolean isTrimLineBeforeMatch() {
105        return trimLineBeforeMatch;
106    }
107
108    /**
109     * Sets whether this will strip trailing spaces before parsing the line.  Defaults to false.
110     * @param trimLineBeforeMatch true if trailing spaces should be stripped, false otherwise
111     */
112    public void setTrimLineBeforeMatch(boolean trimLineBeforeMatch) {
113        this.trimLineBeforeMatch = trimLineBeforeMatch;
114    }
115
116    /**
117     * @return whether the regular expression associated with this object specification will attempt to match the full line or (if false) simply find the pattern somewhere within the line
118     */
119    public boolean isFullMatch() {
120        return fullMatch;
121    }
122
123    /**
124     * Sets whether the regular expression associated with this object specification will attempt to match against the whole line or search for the pattern within the line.  If true, it will match the full line, and this is the default.
125     * @param fullMatch true if match against the full line should be carried out, false otherwise
126     */
127    public void setFullMatch(boolean fullMatch) {
128        this.fullMatch = fullMatch;
129    }
130}