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 */ 017 package org.apache.logging.log4j.core.appender.rolling; 018 019 import org.apache.logging.log4j.core.pattern.ArrayPatternConverter; 020 import org.apache.logging.log4j.core.pattern.DatePatternConverter; 021 import org.apache.logging.log4j.core.pattern.FormattingInfo; 022 import org.apache.logging.log4j.core.pattern.PatternConverter; 023 import org.apache.logging.log4j.core.pattern.PatternParser; 024 025 import java.util.ArrayList; 026 import java.util.Calendar; 027 import java.util.Date; 028 import java.util.List; 029 030 /** 031 * Parse the rollover pattern. 032 */ 033 public class PatternProcessor { 034 035 private static final String KEY = "FileConverter"; 036 037 private static final char YEAR_CHAR = 'y'; 038 private static final char MONTH_CHAR = 'M'; 039 private static final char[] WEEK_CHARS = {'w', 'W'}; 040 private static final char[] DAY_CHARS = {'D', 'd', 'F', 'E'}; 041 private static final char[] HOUR_CHARS = {'H', 'K', 'h', 'k'}; 042 private static final char MINUTE_CHAR = 'm'; 043 private static final char SECOND_CHAR = 's'; 044 private static final char MILLIS_CHAR = 'S'; 045 046 private final ArrayPatternConverter[] patternConverters; 047 private final FormattingInfo[] patternFields; 048 049 private RolloverFrequency frequency = null; 050 051 /** 052 * Constructor. 053 * @param pattern The file pattern. 054 */ 055 public PatternProcessor(String pattern) { 056 PatternParser parser = createPatternParser(); 057 List<PatternConverter> converters = new ArrayList<PatternConverter>(); 058 List<FormattingInfo> fields = new ArrayList<FormattingInfo>(); 059 parser.parse(pattern, converters, fields); 060 FormattingInfo[] infoArray = new FormattingInfo[fields.size()]; 061 patternFields = fields.toArray(infoArray); 062 ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()]; 063 patternConverters = converters.toArray(converterArray); 064 065 for (ArrayPatternConverter converter : patternConverters) { 066 if (converter instanceof DatePatternConverter) { 067 DatePatternConverter dateConverter = (DatePatternConverter) converter; 068 frequency = calculateFrequency(dateConverter.getPattern()); 069 } 070 } 071 } 072 073 /** 074 * Returns the next potential rollover time. 075 * @param current The current time. 076 * @param increment The increment to the next time. 077 * @return the next potential rollover time. 078 */ 079 public long getNextTime(long current, int increment, boolean modulus) { 080 if (frequency == null) { 081 throw new IllegalStateException("Pattern does not contain a date"); 082 } 083 Calendar currentCal = Calendar.getInstance(); 084 currentCal.setTimeInMillis(current); 085 Calendar cal = Calendar.getInstance(); 086 cal.set(currentCal.get(Calendar.YEAR), 0, 1, 0, 0, 0); 087 cal.set(Calendar.MILLISECOND, 0); 088 if (frequency == RolloverFrequency.ANNUALLY) { 089 increment(cal, Calendar.YEAR, increment, modulus); 090 return cal.getTimeInMillis(); 091 } 092 if (frequency == RolloverFrequency.MONTHLY) { 093 increment(cal, Calendar.MONTH, increment, modulus); 094 return cal.getTimeInMillis(); 095 } 096 if (frequency == RolloverFrequency.WEEKLY) { 097 increment(cal, Calendar.WEEK_OF_YEAR, increment, modulus); 098 return cal.getTimeInMillis(); 099 } 100 cal.set(Calendar.DAY_OF_YEAR, currentCal.get(Calendar.DAY_OF_YEAR)); 101 if (frequency == RolloverFrequency.DAILY) { 102 increment(cal, Calendar.DAY_OF_YEAR, increment, modulus); 103 return cal.getTimeInMillis(); 104 } 105 cal.set(Calendar.HOUR, currentCal.get(Calendar.HOUR)); 106 if (frequency == RolloverFrequency.HOURLY) { 107 increment(cal, Calendar.HOUR, increment, modulus); 108 return cal.getTimeInMillis(); 109 } 110 cal.set(Calendar.MINUTE, currentCal.get(Calendar.MINUTE)); 111 if (frequency == RolloverFrequency.EVERY_MINUTE) { 112 increment(cal, Calendar.MINUTE, increment, modulus); 113 return cal.getTimeInMillis(); 114 } 115 cal.set(Calendar.SECOND, currentCal.get(Calendar.SECOND)); 116 if (frequency == RolloverFrequency.EVERY_SECOND) { 117 increment(cal, Calendar.SECOND, increment, modulus); 118 return cal.getTimeInMillis(); 119 } 120 increment(cal, Calendar.MILLISECOND, increment, modulus); 121 return cal.getTimeInMillis(); 122 } 123 124 private void increment(Calendar cal, int type, int increment, boolean modulate) { 125 int interval = modulate ? increment - (cal.get(type) % increment) : increment; 126 cal.add(type, interval); 127 } 128 129 /** 130 * Format file name. 131 * @param buf string buffer to which formatted file name is appended, may not be null. 132 * @param obj object to be evaluated in formatting, may not be null. 133 */ 134 protected final void formatFileName(final StringBuilder buf, final Object obj) { 135 formatFileName(buf, new Date(System.currentTimeMillis()), obj); 136 } 137 138 /** 139 * Format file name. 140 * @param buf string buffer to which formatted file name is appended, may not be null. 141 * @param objects objects to be evaluated in formatting, may not be null. 142 */ 143 protected final void formatFileName(final StringBuilder buf, final Object... objects) { 144 for (int i = 0; i < patternConverters.length; i++) { 145 int fieldStart = buf.length(); 146 patternConverters[i].format(buf, objects); 147 148 if (patternFields[i] != null) { 149 patternFields[i].format(fieldStart, buf); 150 } 151 } 152 } 153 154 private RolloverFrequency calculateFrequency(String pattern) { 155 if (patternContains(pattern, MILLIS_CHAR)) { 156 return RolloverFrequency.EVERY_MILLISECOND; 157 } 158 if (patternContains(pattern, SECOND_CHAR)) { 159 return RolloverFrequency.EVERY_SECOND; 160 } 161 if (patternContains(pattern, MINUTE_CHAR)) { 162 return RolloverFrequency.EVERY_MINUTE; 163 } 164 if (patternContains(pattern, HOUR_CHARS)) { 165 return RolloverFrequency.HOURLY; 166 } 167 if (patternContains(pattern, DAY_CHARS)) { 168 return RolloverFrequency.DAILY; 169 } 170 if (patternContains(pattern, WEEK_CHARS)) { 171 return RolloverFrequency.WEEKLY; 172 } 173 if (patternContains(pattern, MONTH_CHAR)) { 174 return RolloverFrequency.MONTHLY; 175 } 176 if (patternContains(pattern, YEAR_CHAR)) { 177 return RolloverFrequency.ANNUALLY; 178 } 179 return null; 180 } 181 182 private PatternParser createPatternParser() { 183 184 return new PatternParser(null, KEY, null); 185 } 186 187 private boolean patternContains(String pattern, char... chars) { 188 for (char character : chars) { 189 if (patternContains(pattern, character)) { 190 return true; 191 } 192 } 193 return false; 194 } 195 196 private boolean patternContains(String pattern, char character) { 197 return pattern.indexOf(character) >= 0; 198 } 199 }