/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2010-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.syslogd;

import java.lang.reflect.Constructor;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.opennms.netmgt.config.SyslogdConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class SyslogParser {
    private static final Logger LOG = LoggerFactory.getLogger(SyslogParser.class);
    private static final String datePattern="((19|20)\\d{2})-([1-9]|0[1-9]|1[0-2])-(0[1-9]|[1-9]|[12][0-9]|3[01])";
    private Matcher m_matcher = null;
    private final SyslogdConfig m_config;
    private final String m_text;
    private Boolean m_found = null;
    private Boolean m_matched = null;
    private boolean m_traceEnabled = false;
    
    public static SyslogParser getParserInstance(SyslogdConfig config, String text) throws MessageDiscardedException {
        Class<? extends SyslogParser> m_parserClass = null;
        try {
            m_parserClass = Class.forName(config.getParser()).asSubclass(SyslogParser.class);
        } catch (final Exception ex) {
            LOG.debug("Unable to instantiate Syslog parser class specified in config: {}", config.getParser(), ex);
            m_parserClass = CustomSyslogParser.class;
        }

        final SyslogParser retval;
        try {
            Constructor<? extends SyslogParser> m = m_parserClass.getConstructor(SyslogdConfig.class, String.class);
            retval = (SyslogParser)m.newInstance(config, text);
        } catch (final Exception ex) {
            LOG.debug("Unable to get parser for class '{}'", m_parserClass.getName(), ex);
            throw new MessageDiscardedException(ex);
        }

        return retval;
    }

    protected SyslogParser(final SyslogdConfig config, final String text) {
        if (config == null) {
            throw new IllegalArgumentException("Config argument to SyslogParser must not be null");
        } else if (text == null) {
            throw new IllegalArgumentException("Text argument to SyslogParser must not be null");
        }
        m_config = config;
        m_text = text;
        m_traceEnabled = LOG.isTraceEnabled();
    }

    public boolean find() {
        if (m_found == null) {
            getMatcher().reset();
            m_found = getMatcher().find();
        }
        return m_found;
    }

    public boolean matches() {
        if (m_matched == null) {
            getMatcher().reset();
            m_matched = getMatcher().matches();
        }
        return m_matched;
    }

    protected Boolean matched() {
        return m_matched;
    }

    protected SyslogdConfig getConfig() {
        return m_config;
    }

    protected String getText() {
        return m_text;
    }

    protected boolean traceEnabled() {
        return m_traceEnabled;
    }

    /* override this to get your custom pattern */
    protected Pattern getPattern() {
        return Pattern.compile("^.*$");
    }

    /* override this to parse data from the matcher */
    public SyslogMessage parse() throws SyslogParserException {
        final SyslogMessage message = new SyslogMessage();
        message.setMessage(getMatcher().group().trim());
        return message;
    }

    protected Matcher getMatcher() {
        if (m_matcher == null) {
            m_matcher = getPattern().matcher(m_text);
        }
        return m_matcher;
    }

    protected static Date parseDate(final String dateString) {
        try {
            // Date pattern has been crearted and checked inside if loop instead of 
            // parsing date inside the exception class.
            if (dateString.matches(datePattern)) {
                final DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
                df.setTimeZone(TimeZone.getTimeZone("UTC"));
                return df.parse(dateString);
            } else {
                final DateFormat df = new SimpleDateFormat("MMM dd HH:mm:ss", Locale.ROOT);
                df.setTimeZone(TimeZone.getTimeZone("UTC"));
                
                // Ugh, what's a non-lame way of forcing it to parse to "this year"?
                Date date = df.parse(dateString);
                final Calendar c = df.getCalendar();
                c.setTime(date);
                c.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
                return c.getTime();
            }
        } catch (final Exception e) {
            LOG.debug("Unable to parse date '{}'", dateString, e);
            return null;
        }
    }
}

@@ -1431,24 +1431,93 @@
 va.util.
-TimeZone
+Map;%0Aimport java.util.TimeZone;%0Aimport java.util.concurrent.ConcurrentHashMap
 ;%0Aimport
@@ -2140,24 +2140,131 @@
  false;%0A    
+private static final Map%3CString,Class%3C? extends SyslogParser%3E%3E PARSER_CLASSES = new ConcurrentHashMap%3C%3E();%0A
 %0A    public 
@@ -2409,18 +2409,82 @@
 Parser%3E 
-m_
+parserClass = PARSER_CLASSES.get(config.getParser());%0A        if (
 parserCl
@@ -2488,31 +2488,35 @@
 rClass =
+=
  null
-;%0A
+) %7B%0A 
         
 try %7B%0A  
@@ -2499,32 +2499,35 @@
 ull) %7B%0A         
+   
 try %7B%0A          
@@ -2520,34 +2520,36 @@
 y %7B%0A            
-m_
+    
 parserClass = Cl
@@ -2604,32 +2604,105 @@
 gParser.class);%0A
+                PARSER_CLASSES.put(config.getParser(), parserClass);%0A    
         %7D catch 
@@ -2716,32 +2716,36 @@
 Exception ex) %7B%0A
+    
             LOG.
@@ -2856,18 +2856,20 @@
         
-m_
+    
 parserCl
@@ -2900,16 +2900,30 @@
 .class;%0A
+            %7D%0A
         
@@ -3026,18 +3026,16 @@
 er%3E m = 
-m_
 parserCl
@@ -3253,18 +3253,16 @@
  '%7B%7D'%22, 
-m_
 parserCl

Add class caching to SyslogParser to decrease overhead.