001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.converter;
018
019import java.util.regex.Matcher;
020import java.util.regex.Pattern;
021
022import org.apache.camel.Converter;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 * Converter from String syntax to milli seconds.
028 * Code is copied to org.apache.camel.catalog.TimePatternConverter in camel-catalog
029 */
030@Converter
031public final class TimePatternConverter {   
032    private static final Logger LOG = LoggerFactory.getLogger(TimePatternConverter.class);
033    private static final Pattern NUMBERS_ONLY_STRING_PATTERN = Pattern.compile("^[-]?(\\d)+$", Pattern.CASE_INSENSITIVE);
034    private static final Pattern HOUR_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))h(our(s)?)?", Pattern.CASE_INSENSITIVE);
035    private static final Pattern MINUTES_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))m(in(ute(s)?)?)?", Pattern.CASE_INSENSITIVE);
036    private static final Pattern SECONDS_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))s(ec(ond)?(s)?)?", Pattern.CASE_INSENSITIVE);
037
038    /**
039     * Utility classes should not have a public constructor.
040     */
041    private TimePatternConverter() {
042    }
043    
044    @Converter
045    public static long toMilliSeconds(String source) throws IllegalArgumentException {
046        // quick conversion if its only digits
047        boolean digit = true;
048        for (int i = 0; i < source.length(); i++) {
049            char ch = source.charAt(i);
050            // special for fist as it can be negative number
051            if (i == 0 && ch == '-') {
052                continue;
053            }
054            // quick check if its 0..9
055            if (ch < '0' || ch > '9') {
056                digit = false;
057                break;
058            }
059        }
060        if (digit) {
061            return Long.valueOf(source);
062        }
063
064        long milliseconds = 0;
065        boolean foundFlag = false;
066
067        checkCorrectnessOfPattern(source);
068        Matcher matcher;
069
070        matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source);
071        if (matcher.find()) {
072            // Note: This will also be used for regular numeric strings. 
073            //       This String -> long converter will be used for all strings.
074            milliseconds = Long.valueOf(source);
075        } else {            
076            matcher = createMatcher(HOUR_REGEX_PATTERN, source);
077            if (matcher.find()) {
078                milliseconds = milliseconds + (3600000 * Long.valueOf(matcher.group(1)));
079                foundFlag = true;
080            }
081            
082            matcher = createMatcher(MINUTES_REGEX_PATTERN, source);
083            if (matcher.find()) {
084                long minutes = Long.valueOf(matcher.group(1));
085                if ((minutes > 59) && foundFlag) {
086                    throw new IllegalArgumentException("Minutes should contain a valid value between 0 and 59: " + source);
087                }
088                foundFlag = true;
089                milliseconds = milliseconds + (60000 * minutes);
090            }
091               
092            matcher = createMatcher(SECONDS_REGEX_PATTERN, source);
093            if (matcher.find()) {
094                long seconds = Long.valueOf(matcher.group(1));
095                if ((seconds > 59) && foundFlag) {
096                    throw new IllegalArgumentException("Seconds should contain a valid value between 0 and 59: " + source);
097                }
098                foundFlag = true;
099                milliseconds = milliseconds + (1000 * seconds);
100            }      
101            
102            // No pattern matched... initiating fallback check and conversion (if required). 
103            // The source at this point may contain illegal values or special characters 
104            if (!foundFlag) {
105                milliseconds = Long.valueOf(source);
106            }
107        }       
108        
109        LOG.trace("source: {} milliseconds: ", source, milliseconds);
110        
111        return milliseconds;
112    }
113
114    private static void checkCorrectnessOfPattern(String source) {
115        //replace only numbers once
116        Matcher matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source);
117        String replaceSource = matcher.replaceFirst("");
118
119        //replace hour string once
120        matcher = createMatcher(HOUR_REGEX_PATTERN, replaceSource);
121        if (matcher.find() && matcher.find()) {
122            throw new IllegalArgumentException("Hours should not be specified more then once: " + source);
123        }
124        replaceSource = matcher.replaceFirst("");
125
126        //replace minutes once
127        matcher = createMatcher(MINUTES_REGEX_PATTERN, replaceSource);
128        if (matcher.find() && matcher.find()) {
129            throw new IllegalArgumentException("Minutes should not be specified more then once: " + source);
130        }
131        replaceSource = matcher.replaceFirst("");
132
133        //replace seconds once
134        matcher = createMatcher(SECONDS_REGEX_PATTERN, replaceSource);
135        if (matcher.find() && matcher.find()) {
136            throw new IllegalArgumentException("Seconds should not be specified more then once: " + source);
137        }
138        replaceSource = matcher.replaceFirst("");
139
140        if (replaceSource.length() > 0) {
141            throw new IllegalArgumentException("Illegal characters: " + source);
142        }
143    }
144
145    private static Matcher createMatcher(Pattern pattern, String source) {
146        return pattern.matcher(source);        
147    }    
148}