/*
 * Copyright (C) 2009 Google 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 cn.emay.sdk.util.json.gson;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Iterator;
import java.util.NoSuchElementException;
import cn.emay.sdk.util.json.gson.internal.Streams;
import cn.emay.sdk.util.json.gson.stream.JsonReader;
import cn.emay.sdk.util.json.gson.stream.JsonToken;
import cn.emay.sdk.util.json.gson.stream.MalformedJsonException;
/**
 * A streaming parser that allows reading of multiple {@link JsonElement}s from
 * the specified reader asynchronously.
 * 
 * 
 * This class is conditionally thread-safe (see Item 70, Effective Java second
 * edition). To properly use this class across multiple threads, you will need
 * to add some external synchronization. For example:
 * 
 * 
 * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
 * JsonElement element;
 * synchronized (parser) { // synchronize on an object shared by threads
 * 	if (parser.hasNext()) {
 * 		element = parser.next();
 * 	}
 * }
 * 
 *
 * @author Inderjeet Singh
 * @author Joel Leitch
 * @since 1.4
 */
public final class JsonStreamParser implements Iterator {
	private final JsonReader parser;
	private final Object lock;
	/**
	 * @param json
	 *            The string containing JSON elements concatenated to each other.
	 * @since 1.4
	 */
	public JsonStreamParser(String json) {
		this(new StringReader(json));
	}
	/**
	 * @param reader
	 *            The data stream containing JSON elements concatenated to each
	 *            other.
	 * @since 1.4
	 */
	public JsonStreamParser(Reader reader) {
		parser = new JsonReader(reader);
		parser.setLenient(true);
		lock = new Object();
	}
	/**
	 * Returns the next available {@link JsonElement} on the reader. Null if none
	 * available.
	 * 
	 * @return the next available {@link JsonElement} on the reader. Null if none
	 *         available.
	 * @throws JsonParseException
	 *             if the incoming stream is malformed JSON.
	 * @since 1.4
	 */
	public JsonElement next() throws JsonParseException {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		try {
			return Streams.parse(parser);
		} catch (StackOverflowError e) {
			throw new JsonParseException("Failed parsing JSON source to Json", e);
		} catch (OutOfMemoryError e) {
			throw new JsonParseException("Failed parsing JSON source to Json", e);
		} catch (JsonParseException e) {
			throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
		}
	}
	/**
	 * Returns true if a {@link JsonElement} is available on the input for
	 * consumption
	 * 
	 * @return true if a {@link JsonElement} is available on the input, false
	 *         otherwise
	 * @since 1.4
	 */
	public boolean hasNext() {
		synchronized (lock) {
			try {
				return parser.peek() != JsonToken.END_DOCUMENT;
			} catch (MalformedJsonException e) {
				throw new JsonSyntaxException(e);
			} catch (IOException e) {
				throw new JsonIOException(e);
			}
		}
	}
	/**
	 * This optional {@link Iterator} method is not relevant for stream parsing and
	 * hence is not implemented.
	 * 
	 * @since 1.4
	 */
	public void remove() {
		throw new UnsupportedOperationException();
	}
}