/*
* Druid - a distributed column store.
* Copyright 2012 - 2015 Metamarkets Group Inc.
*
* 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 io.druid.data.input.impl;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Charsets;
import com.metamx.common.parsers.ParseException;
import com.metamx.common.parsers.Parser;
import io.druid.data.input.ByteBufferInputRowParser;
import io.druid.data.input.InputRow;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Map;
/**
*/
public class StringInputRowParser implements ByteBufferInputRowParser
{
private static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
private final ParseSpec parseSpec;
private final MapInputRowParser mapParser;
private final Parser<String, Object> parser;
private final Charset charset;
private CharBuffer chars = null;
@JsonCreator
public StringInputRowParser(
@JsonProperty("parseSpec") ParseSpec parseSpec,
@JsonProperty("encoding") String encoding
)
{
this.parseSpec = parseSpec;
this.mapParser = new MapInputRowParser(parseSpec);
this.parser = parseSpec.makeParser();
if (encoding != null) {
this.charset = Charset.forName(encoding);
} else {
this.charset = DEFAULT_CHARSET;
}
}
@Deprecated
public StringInputRowParser(ParseSpec parseSpec)
{
this(parseSpec, null);
}
@Override
public InputRow parse(ByteBuffer input)
{
return parseMap(buildStringKeyMap(input));
}
@JsonProperty
@Override
public ParseSpec getParseSpec()
{
return parseSpec;
}
@JsonProperty
public String getEncoding()
{
return charset.name();
}
@Override
public StringInputRowParser withParseSpec(ParseSpec parseSpec)
{
return new StringInputRowParser(parseSpec, getEncoding());
}
private Map<String, Object> buildStringKeyMap(ByteBuffer input)
{
int payloadSize = input.remaining();
if (chars == null || chars.remaining() < payloadSize) {
chars = CharBuffer.allocate(payloadSize);
}
final CoderResult coderResult = charset.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.decode(input, chars, true);
Map<String, Object> theMap;
if (coderResult.isUnderflow()) {
chars.flip();
try {
theMap = parseString(chars.toString());
}
finally {
chars.clear();
}
} else {
throw new ParseException("Failed with CoderResult[%s]", coderResult);
}
return theMap;
}
private Map<String, Object> parseString(String inputString)
{
return parser.parse(inputString);
}
public InputRow parse(String input)
{
return parseMap(parseString(input));
}
private InputRow parseMap(Map<String, Object> theMap)
{
return mapParser.parse(theMap);
}
}