/******************************************************************************* * This file is part of RedReader. * * RedReader is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RedReader 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RedReader. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package org.quantumbadger.redreader.jsonwrap; import android.support.annotation.IntDef; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An abstract class, extended by objects which may be partially received/parsed * at the time they are used. * */ public abstract class JsonBuffered { /** * This object or array is not fully parsed yet. Because of this, * attempts to access some properties or elements may cause the current * thread to block. */ public static final int STATUS_LOADING = 0; /** * This object or array is now fully loaded. All properties and elements * can be accessed without blocking. */ public static final int STATUS_LOADED = 1; /** * There was a problem parsing this object/array (or one of its * descendants). Attempting to access any of the data it contains may * cause an exception. */ public static final int STATUS_FAILED = 2; @IntDef({STATUS_LOADING, STATUS_LOADED, STATUS_FAILED}) @Retention(RetentionPolicy.SOURCE) public @interface Status {} private volatile @Status int status = STATUS_LOADING; private Throwable failReason = null; /** * @return The current status of this object: STATUS_LOADING, STATUS_LOADED, or STATUS_FAILED. */ public final @Status int getStatus() { return status; } /** * Causes the current thread to wait until this object and all its children * fully received. * * @return The final status of the object (either STATUS_LOADED or STATUS_FAILED). * @throws InterruptedException */ public final synchronized @Status int join() throws InterruptedException { while(status == STATUS_LOADING) { wait(); } return status; } private synchronized void setLoaded() { status = STATUS_LOADED; notifyAll(); } private synchronized void setFailed(final Throwable t) { status = STATUS_FAILED; failReason = t; notifyAll(); } /** * @return If this object or one of its children failed to parse, the * exception that occurred at the time of failure is returned. * Otherwise, null. */ public final Throwable getFailReason() { return failReason; } protected final void throwFailReasonException() throws IOException { final Throwable t = getFailReason(); if(t instanceof JsonParseException) throw (JsonParseException)t; else if(t instanceof IOException) throw (IOException)t; else throw new RuntimeException(t); } protected final void build(final JsonParser jp) throws IOException { try { buildBuffered(jp); setLoaded(); } catch (final IOException e) { setFailed(e); throw e; } catch (final Throwable t) { setFailed(t); throw new RuntimeException(t); } } protected abstract void buildBuffered(JsonParser jp) throws IOException; @Override public String toString() { final StringBuilder sb = new StringBuilder(); try { prettyPrint(0, sb); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } protected abstract void prettyPrint(int indent, StringBuilder sb) throws InterruptedException, IOException; }