/** * 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. */ package org.apache.drill.exec.store.easy.json.reader; import io.netty.buffer.DrillBuf; import java.io.IOException; import java.io.InputStream; import org.apache.drill.exec.store.easy.json.JsonProcessor; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.TreeTraversingParser; import com.google.common.base.Preconditions; import org.apache.drill.common.exceptions.UserException; public abstract class BaseJsonProcessor implements JsonProcessor { private static final ObjectMapper MAPPER = new ObjectMapper().configure( JsonParser.Feature.ALLOW_COMMENTS, true).configure( JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); private static final String JACKSON_PARSER_EOF_FILE_MSG = "Unexpected end-of-input:"; public static enum JsonExceptionProcessingState { END_OF_STREAM, PROC_SUCCEED } protected JsonParser parser; protected DrillBuf workBuf; protected JsonToken lastSeenJsonToken = null; boolean ignoreJSONParseErrors = false; // default False public boolean ignoreJSONParseError() { return ignoreJSONParseErrors; } public void setIgnoreJSONParseErrors(boolean ignoreJSONParseErrors) { this.ignoreJSONParseErrors = ignoreJSONParseErrors; } public BaseJsonProcessor(DrillBuf workBuf) { workBuf = Preconditions.checkNotNull(workBuf); } @Override public void setSource(InputStream is) throws IOException { parser = MAPPER.getFactory().createParser(is); } @Override public void setSource(JsonNode node) { this.parser = new TreeTraversingParser(node); } @Override public UserException.Builder getExceptionWithContext( UserException.Builder exceptionBuilder, String field, String msg, Object... args) { if (msg != null) { exceptionBuilder.message(msg, args); } if (field != null) { exceptionBuilder.pushContext("Field ", field); } exceptionBuilder.pushContext("Column ", parser.getCurrentLocation().getColumnNr() + 1).pushContext("Line ", parser.getCurrentLocation().getLineNr()); return exceptionBuilder; } @Override public UserException.Builder getExceptionWithContext(Throwable e, String field, String msg, Object... args) { UserException.Builder exceptionBuilder = UserException.dataReadError(e); return getExceptionWithContext(exceptionBuilder, field, msg, args); } /* * DRILL - 4653 This method processes JSON tokens until it reaches end of the * current line when it processes start of a new JSON line { - return * PROC_SUCCEED when it sees EOF the stream - there may not be a closing } */ protected JsonExceptionProcessingState processJSONException() throws IOException { while (!parser.isClosed()) { try { JsonToken currentToken = parser.nextToken(); if(currentToken == JsonToken.START_OBJECT && (lastSeenJsonToken == JsonToken.END_OBJECT || lastSeenJsonToken == null)) { lastSeenJsonToken =currentToken; break; } lastSeenJsonToken =currentToken; } catch (com.fasterxml.jackson.core.JsonParseException ex1) { if (ex1.getOriginalMessage().startsWith(JACKSON_PARSER_EOF_FILE_MSG)) { return JsonExceptionProcessingState.END_OF_STREAM; } continue; } } return JsonExceptionProcessingState.PROC_SUCCEED; } }