001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.rolling.helper; 015 016import java.time.Instant; 017import java.util.Date; 018import java.util.HashMap; 019import java.util.Map; 020 021import ch.qos.logback.core.Context; 022import ch.qos.logback.core.pattern.Converter; 023import ch.qos.logback.core.pattern.ConverterUtil; 024import ch.qos.logback.core.pattern.LiteralConverter; 025import ch.qos.logback.core.pattern.parser.Node; 026import ch.qos.logback.core.pattern.parser.Parser; 027import ch.qos.logback.core.spi.ScanException; 028import ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil; 029import ch.qos.logback.core.spi.ContextAwareBase; 030 031/** 032 * After parsing file name patterns, given a number or a date, instances of this 033 * class can be used to compute a file name according to the file name pattern 034 * and the current date or integer. 035 * 036 * @author Ceki Gülcü 037 * 038 */ 039public class FileNamePattern extends ContextAwareBase { 040 041 static final Map<String, String> CONVERTER_MAP = new HashMap<String, String>(); 042 static { 043 CONVERTER_MAP.put(IntegerTokenConverter.CONVERTER_KEY, IntegerTokenConverter.class.getName()); 044 CONVERTER_MAP.put(DateTokenConverter.CONVERTER_KEY, DateTokenConverter.class.getName()); 045 } 046 047 String pattern; 048 Converter<Object> headTokenConverter; 049 050 public FileNamePattern(String patternArg, Context contextArg) { 051 // the pattern is slashified 052 setPattern(FileFilterUtil.slashify(patternArg)); 053 setContext(contextArg); 054 parse(); 055 ConverterUtil.startConverters(this.headTokenConverter); 056 } 057 058 void parse() { 059 try { 060 // http://jira.qos.ch/browse/LOGBACK-197 061 // we escape ')' for parsing purposes. Note that the original pattern is 062 // preserved 063 // because it is shown to the user in status messages. We don't want the escaped 064 // version 065 // to leak out. 066 String patternForParsing = escapeRightParantesis(pattern); 067 Parser<Object> p = new Parser<Object>(patternForParsing, new AlmostAsIsEscapeUtil()); 068 p.setContext(context); 069 Node t = p.parse(); 070 this.headTokenConverter = p.compile(t, CONVERTER_MAP); 071 072 } catch (ScanException sce) { 073 addError("Failed to parse pattern \"" + pattern + "\".", sce); 074 } 075 } 076 077 String escapeRightParantesis(String in) { 078 return pattern.replace(")", "\\)"); 079 } 080 081 public String toString() { 082 return pattern; 083 } 084 085 @Override 086 public int hashCode() { 087 final int prime = 31; 088 int result = 1; 089 result = prime * result + ((pattern == null) ? 0 : pattern.hashCode()); 090 return result; 091 } 092 093 @Override 094 public boolean equals(Object obj) { 095 if (this == obj) 096 return true; 097 if (obj == null) 098 return false; 099 if (getClass() != obj.getClass()) 100 return false; 101 FileNamePattern other = (FileNamePattern) obj; 102 if (pattern == null) { 103 if (other.pattern != null) 104 return false; 105 } else if (!pattern.equals(other.pattern)) 106 return false; 107 return true; 108 } 109 110 public DateTokenConverter<Object> getPrimaryDateTokenConverter() { 111 Converter<Object> p = headTokenConverter; 112 113 while (p != null) { 114 if (p instanceof DateTokenConverter) { 115 DateTokenConverter<Object> dtc = (DateTokenConverter<Object>) p; 116 // only primary converters should be returned as 117 if (dtc.isPrimary()) 118 return dtc; 119 } 120 121 p = p.getNext(); 122 } 123 124 return null; 125 } 126 127 public IntegerTokenConverter getIntegerTokenConverter() { 128 Converter<Object> p = headTokenConverter; 129 130 while (p != null) { 131 if (p instanceof IntegerTokenConverter) { 132 return (IntegerTokenConverter) p; 133 } 134 135 p = p.getNext(); 136 } 137 return null; 138 } 139 140 public boolean hasIntegerTokenCOnverter() { 141 IntegerTokenConverter itc = getIntegerTokenConverter(); 142 return itc != null; 143 } 144 145 public String convertMultipleArguments(Object... objectList) { 146 StringBuilder buf = new StringBuilder(); 147 Converter<Object> c = headTokenConverter; 148 while (c != null) { 149 if (c instanceof MonoTypedConverter) { 150 MonoTypedConverter monoTyped = (MonoTypedConverter) c; 151 for (Object o : objectList) { 152 if (monoTyped.isApplicable(o)) { 153 buf.append(c.convert(o)); 154 } 155 } 156 } else { 157 buf.append(c.convert(objectList)); 158 } 159 c = c.getNext(); 160 } 161 return buf.toString(); 162 } 163 164 public String convert(Object o) { 165 StringBuilder buf = new StringBuilder(); 166 Converter<Object> p = headTokenConverter; 167 while (p != null) { 168 buf.append(p.convert(o)); 169 p = p.getNext(); 170 } 171 return buf.toString(); 172 } 173 174 public String convertInt(int i) { 175 return convert(i); 176 } 177 178 public void setPattern(String pattern) { 179 if (pattern != null) { 180 // Trailing spaces in the pattern are assumed to be undesired. 181 this.pattern = pattern.trim(); 182 } 183 } 184 185 public String getPattern() { 186 return pattern; 187 } 188 189 /** 190 * Given date, convert this instance to a regular expression. 191 * 192 * Used to compute sub-regex when the pattern has both %d and %i, and the date 193 * is known. 194 * 195 * @param date - known date 196 */ 197 public String toRegexForFixedDate(Date date) { 198 StringBuilder buf = new StringBuilder(); 199 Converter<Object> p = headTokenConverter; 200 while (p != null) { 201 if (p instanceof LiteralConverter) { 202 buf.append(p.convert(null)); 203 } else if (p instanceof IntegerTokenConverter) { 204 buf.append("(\\d+)"); 205 } else if (p instanceof DateTokenConverter) { 206 buf.append(p.convert(date)); 207 } 208 p = p.getNext(); 209 } 210 return buf.toString(); 211 } 212 213 214 /** 215 * Given date, convert this instance to a regular expression. 216 * 217 * Used to compute sub-regex when the pattern has both %d and %i, and the date 218 * is known. 219 * 220 * @param instant - known instant 221 */ 222 public String toRegexForFixedDate(Instant instant) { 223 StringBuilder buf = new StringBuilder(); 224 Converter<Object> p = headTokenConverter; 225 while (p != null) { 226 if (p instanceof LiteralConverter) { 227 buf.append(p.convert(null)); 228 } else if (p instanceof IntegerTokenConverter) { 229 buf.append("(\\d+)"); 230 } else if (p instanceof DateTokenConverter) { 231 buf.append(p.convert(instant)); 232 } 233 p = p.getNext(); 234 } 235 return buf.toString(); 236 } 237 238 239 /** 240 * Given date, convert this instance to a regular expression 241 */ 242 public String toRegex() { 243 StringBuilder buf = new StringBuilder(); 244 Converter<Object> p = headTokenConverter; 245 while (p != null) { 246 if (p instanceof LiteralConverter) { 247 buf.append(p.convert(null)); 248 } else if (p instanceof IntegerTokenConverter) { 249 buf.append("\\d+"); 250 } else if (p instanceof DateTokenConverter) { 251 DateTokenConverter<Object> dtc = (DateTokenConverter<Object>) p; 252 buf.append(dtc.toRegex()); 253 } 254 p = p.getNext(); 255 } 256 return buf.toString(); 257 } 258}