package org.apache.solr.handler.dataimport; import static org.apache.solr.handler.dataimport.DataImportHandlerException.SEVERE; import static org.apache.solr.handler.dataimport.DataImportHandlerException.wrapAndThrow; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.WeakHashMap; import org.apache.solr.handler.dataimport.config.EntityField; import org.apache.solr.util.DateMathParser; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * <p>Formats values using a given date format. </p> * <p>Pass three parameters: * <ul> * <li>An {@link EntityField} or a date expression to be parsed with * the {@link DateMathParser} class If the value is in a String, * then it is assumed to be a datemath expression, otherwise it * resolved using a {@link VariableResolver} instance</li> * <li>A date format see {@link SimpleDateFormat} for the syntax.</li> * <li>The {@link Locale} to parse. * (optional. Defaults to the Root Locale) </li> * </ul> * </p> */ public class DateFormatEvaluator extends Evaluator { public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; Map<DateFormatCacheKey, SimpleDateFormat> cache = new WeakHashMap<>(); Map<String, Locale> availableLocales = new HashMap<>(); Set<String> availableTimezones = new HashSet<>(); class DateFormatCacheKey { DateFormatCacheKey(Locale l, TimeZone tz, String df) { this.locale = l; this.timezone = tz; this.dateFormat = df; } Locale locale; TimeZone timezone; String dateFormat; } public DateFormatEvaluator() { for (Locale locale : Locale.getAvailableLocales()) { availableLocales.put(locale.toString(), locale); } for (String tz : TimeZone.getAvailableIDs()) { availableTimezones.add(tz); } } private SimpleDateFormat getDateFormat(String pattern, TimeZone timezone, Locale locale) { DateFormatCacheKey dfck = new DateFormatCacheKey(locale, timezone, pattern); SimpleDateFormat sdf = cache.get(dfck); if(sdf == null) { sdf = new SimpleDateFormat(pattern, locale); sdf.setTimeZone(timezone); cache.put(dfck, sdf); } return sdf; } @Override public String evaluate(String expression, Context context) { List<Object> l = parseParams(expression, context.getVariableResolver()); if (l.size() < 2 || l.size() > 4) { throw new DataImportHandlerException(SEVERE, "'formatDate()' must have two, three or four parameters "); } Object o = l.get(0); Object format = l.get(1); if (format instanceof VariableWrapper) { VariableWrapper wrapper = (VariableWrapper) format; o = wrapper.resolve(); format = o.toString(); } Locale locale = Locale.ROOT; if(l.size()>2) { Object localeObj = l.get(2); String localeStr = null; if (localeObj instanceof VariableWrapper) { localeStr = ((VariableWrapper) localeObj).resolve().toString(); } else { localeStr = localeObj.toString(); } locale = availableLocales.get(localeStr); if(locale==null) { throw new DataImportHandlerException(SEVERE, "Unsupported locale: " + localeStr); } } TimeZone tz = TimeZone.getDefault(); if(l.size()==4) { Object tzObj = l.get(3); String tzStr = null; if (tzObj instanceof VariableWrapper) { tzStr = ((VariableWrapper) tzObj).resolve().toString(); } else { tzStr = tzObj.toString(); } if(availableTimezones.contains(tzStr)) { tz = TimeZone.getTimeZone(tzStr); } else { throw new DataImportHandlerException(SEVERE, "Unsupported Timezone: " + tzStr); } } String dateFmt = format.toString(); SimpleDateFormat fmt = getDateFormat(dateFmt, tz, locale); Date date = null; if (o instanceof VariableWrapper) { VariableWrapper variableWrapper = (VariableWrapper) o; Object variableval = variableWrapper.resolve(); if (variableval instanceof Date) { date = (Date) variableval; } else { String s = variableval.toString(); try { date = getDateFormat(DEFAULT_DATE_FORMAT, tz, locale).parse(s); } catch (ParseException exp) { wrapAndThrow(SEVERE, exp, "Invalid expression for date"); } } } else { String datemathfmt = o.toString(); datemathfmt = datemathfmt.replaceAll("NOW", ""); try { date = getDateMathParser(locale, tz).parseMath(datemathfmt); } catch (ParseException e) { wrapAndThrow(SEVERE, e, "Invalid expression for date"); } } return fmt.format(date); } static DateMathParser getDateMathParser(Locale l, TimeZone tz) { return new DateMathParser(tz, l) { @Override public Date getNow() { return new Date(); } }; } }