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