Java tutorial
/* * Copyright 2014-2017 JKOOL, LLC. * * Licensed 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. */ package com.jkoolcloud.tnt4j.streams.parsers; import java.text.ParseException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.StrMatcher; import org.apache.commons.lang3.text.StrTokenizer; import com.jkoolcloud.tnt4j.core.OpLevel; import com.jkoolcloud.tnt4j.sink.DefaultEventSinkFactory; import com.jkoolcloud.tnt4j.sink.EventSink; import com.jkoolcloud.tnt4j.streams.configure.ParserProperties; import com.jkoolcloud.tnt4j.streams.fields.ActivityFieldLocator; import com.jkoolcloud.tnt4j.streams.fields.ActivityInfo; import com.jkoolcloud.tnt4j.streams.inputs.TNTInputStream; import com.jkoolcloud.tnt4j.streams.utils.StreamsResources; /** * Implements an activity data parser that assumes each activity data item is a token-separated string of fields, where * each field is represented by a name/value pair and the name is used to map each field into its corresponding activity * field. The field-separator and the name/value separator can both be customized. * <p> * This parser supports the following properties (in addition to those supported by {@link GenericActivityParser}): * <ul> * <li>FieldDelim - fields separator. (Optional)</li> * <li>ValueDelim - value delimiter. (Optional)</li> * <li>Pattern - pattern used to determine which types of activity data string this parser supports. When {@code null}, * all strings are assumed to match the format supported by this parser. (Optional)</li> * <li>StripQuotes - whether surrounding double quotes should be stripped from extracted data values. (Optional)</li> * </ul> * * @version $Revision: 1 $ */ public class ActivityNameValueParser extends GenericActivityParser<Map<String, String>> { private static final EventSink LOGGER = DefaultEventSinkFactory.defaultEventSink(ActivityNameValueParser.class); /** * Contains the field separator (set by {@code FieldDelim} property) - Default: * "{@value com.jkoolcloud.tnt4j.streams.parsers.GenericActivityParser#DEFAULT_DELIM}" */ protected StrMatcher fieldDelim = StrMatcher.charSetMatcher(DEFAULT_DELIM); /** * Contains the name/value separator (set by {@code ValueDelim} property) - Default: "=" */ protected String valueDelim = "="; // NON-NLS /** * Contains the pattern used to determine which types of activity data string this parser supports (set by * {@code Pattern} property). When {@code null}, all strings are assumed to match the format supported by this * parser. */ protected Pattern pattern = null; /** * Indicates whether surrounding double quotes should be stripped from extracted data values (set by * {@code StripQuotes} property) - default: {@code true} */ protected boolean stripQuotes = true; /** * Constructs a new ActivityNameValueParser. */ public ActivityNameValueParser() { super(); } @Override protected EventSink logger() { return LOGGER; } @Override public void setProperties(Collection<Map.Entry<String, String>> props) throws Exception { if (props == null) { return; } super.setProperties(props); for (Map.Entry<String, String> prop : props) { String name = prop.getKey(); String value = prop.getValue(); if (ParserProperties.PROP_FLD_DELIM.equals(name)) { fieldDelim = StringUtils.isEmpty(value) ? null : StrMatcher.charSetMatcher(value); logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.setting"), name, value); } else if (ParserProperties.PROP_VAL_DELIM.equals(name)) { valueDelim = value; logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.setting"), name, value); } else if (ParserProperties.PROP_PATTERN.equals(name)) { if (StringUtils.isNotEmpty(value)) { pattern = Pattern.compile(value); logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.setting"), name, value); } } else if (ParserProperties.PROP_STRIP_QUOTES.equals(name)) { stripQuotes = Boolean.parseBoolean(value); logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.setting"), name, value); } } } @Override public ActivityInfo parse(TNTInputStream<?, ?> stream, Object data) throws IllegalStateException, ParseException { if (fieldDelim == null) { throw new IllegalStateException(StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityNameValueParser.no.field.delimiter")); } if (valueDelim == null) { throw new IllegalStateException(StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityNameValueParser.no.value.delimiter")); } return super.parse(stream, data); } @Override protected ActivityContext prepareItem(TNTInputStream<?, ?> stream, Object data) throws ParseException { String dataStr = getNextActivityString(data); if (StringUtils.isEmpty(dataStr)) { return null; } logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.splitting.string"), dataStr); if (pattern != null) { Matcher matcher = pattern.matcher(dataStr); if (matcher == null || !matcher.matches()) { logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.input.not.match"), getName(), pattern.pattern()); return null; } } StrTokenizer tk = stripQuotes ? new StrTokenizer(dataStr, fieldDelim, StrMatcher.doubleQuoteMatcher()) : new StrTokenizer(dataStr, fieldDelim); tk.setIgnoreEmptyTokens(false); String[] fields = tk.getTokenArray(); if (ArrayUtils.isEmpty(fields)) { logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.no.fields")); return null; } logger().log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.split"), fields.length); Map<String, String> nameValues = new HashMap<>(fields.length); for (String field : fields) { if (field != null) { String[] nv = field.split(Pattern.quote(valueDelim)); if (ArrayUtils.isNotEmpty(nv)) { nameValues.put(nv[0], nv.length > 1 ? nv[1].trim() : ""); } logger().log(OpLevel.TRACE, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityNameValueParser.found"), field); } } ActivityContext cData = new ActivityContext(stream, data, nameValues); cData.setMessage(getRawDataAsMessage(nameValues)); return cData; } /** * Gets field raw data value resolved by locator. * * @param locator * activity field locator * @param cData * activity object name/value pairs map * @param formattingNeeded * flag to set if value formatting is not needed * @return raw value resolved by locator, or {@code null} if value is not resolved */ @Override protected Object resolveLocatorValue(ActivityFieldLocator locator, ActivityContext cData, AtomicBoolean formattingNeeded) { Object val = null; String locStr = locator.getLocator(); val = cData.getData().get(locStr); // NOTE: locStr == null? return val; } }