// This file is part of PleoCommand: // Interactively control Pleo with psychobiological parameters // // Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de // // This program 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 2 // of the License, or (at your option) any later version. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Boston, USA. package pleocmd.pipe; import java.io.IOException; import pleocmd.Log; import pleocmd.exc.InternalException; import pleocmd.exc.PipeException; import pleocmd.exc.StateException; /** * This class helps sub classes to deny calling methods according to a state in * which the object currently is.<br> * All methods which should be protected must have an appropriate ensure...() * call as their first statement. * * @author oliver */ public abstract class StateHandling { /** * All valid states an object can be in. */ protected enum State { /** * This object is currently being constructor (i.e. the constructor has * not yet be finished) */ Constructing, /** * This object is constructed but not yet configured. */ Constructed, /** * This object is constructed and configured and therefore ready to be * initialized. */ Configured, /** * This object is constructed, configured and initialized and therefore * ready to be used. */ Initialized } private State state = State.Constructing; /** * Must be called as the last statement in all constructors of all sub * classes. */ public final void constructed() { try { ensureConstructing(); setState(State.Constructed); } catch (final StateException e) { throw new InternalException(e); } } /** * @return current {@link State} of this object. */ public final State getState() { return state; } /** * Only allowed changes are:<br> * Constructing -> Constructed -> Configured <-> Initialized * * @param from * old {@link State} * @param to * new {@link State} * @return true if change is allowed */ @SuppressWarnings("incomplete-switch") private static boolean checkValidChange(final State from, final State to) { switch (from) { case Constructing: switch (to) { case Constructing: case Constructed: return true; } break; case Constructed: switch (to) { case Constructed: case Configured: return true; } break; case Configured: case Initialized: switch (to) { case Configured: case Initialized: return true; } break; } return false; } public final void configure() throws PipeException { ensureConstructed(); try { configure0(); setState(State.Configured); } catch (final IOException e) { throw new PipeException(this, true, e, "Cannot configure '%s'", toString()); } } /** * Can contain special code which should be invoked in sub-classes during * configuration. * * @throws PipeException * if configuration fails * @throws IOException * if configuration fails */ protected void configure0() throws PipeException, IOException { // do nothing by default } public final void init() throws PipeException { ensureConfigured(); try { init0(); setState(State.Initialized); } catch (final IOException e) { throw new PipeException(this, true, e, "Cannot initialize '%s'", toString()); } } /** * Can contain special code which should be invoked in sub-classes during * initialization. * * @throws PipeException * if initialization fails * @throws IOException * if initialization fails */ protected void init0() throws PipeException, IOException { // do nothing by default } public final void close() throws PipeException { ensureInitialized(); try { setState(State.Configured); close0(); } catch (final IOException e) { throw new PipeException(this, true, e, "Cannot close '%s'", toString()); } } /** * Can contain special code which should be invoked in sub-classes during * closing. * * @throws PipeException * if closing fails * @throws IOException * if closing fails */ protected void close0() throws PipeException, IOException { // do nothing by default } private void setState(final State state) throws StateException { if (!checkValidChange(this.state, state)) throw new StateException(this, true, "Cannot change state from '%s' to '%s'", this.state, state); Log.detail("'%s' changed state: '%s' => '%s'", toString(), this.state, state); this.state = state; } private void throwException(final String msg) throws StateException { throw new StateException(this, true, "'%s' is in a wrong state: %s", toString(), msg); } private void throwUnknownState() throws StateException { throw new StateException(this, true, "'%s' is in an unknown state: %s", toString(), state); } /** * Ensures that this object is in a valid {@link State}. * * @throws StateException * if the object is already constructed */ public final void ensureConstructing() throws StateException { switch (state) { case Constructing: break; case Constructed: case Configured: case Initialized: throwException("Already constructed"); break; default: throwUnknownState(); break; } } /** * Ensures that this object is in a valid {@link State}. * * @throws StateException * if the object is being constructed or already initialized */ public final void ensureConstructed() throws StateException { switch (state) { case Constructing: throwException("Constructing"); break; case Constructed: case Configured: break; case Initialized: throwException("Already initialized"); break; default: throwUnknownState(); break; } } /** * Ensures that this object is in a valid {@link State}. * * @throws StateException * if the object is being constructed, not yet configured or * already initialized */ public final void ensureConfigured() throws StateException { switch (state) { case Constructing: throwException("Constructing"); break; case Constructed: throwException("Not configured"); break; case Configured: break; case Initialized: throwException("Already initialized"); break; default: throwUnknownState(); break; } } /** * Ensures that this object is in a valid {@link State}. * * @throws StateException * if the object is not already initialized */ public final void ensureInitialized() throws StateException { switch (state) { case Constructing: case Constructed: case Configured: throwException("Not initialized"); break; case Initialized: break; default: throwUnknownState(); break; } } /** * Ensures that this object is in a valid {@link State}. * * @throws StateException * if the object is currently initialized */ public final void ensureNoLongerInitialized() throws StateException { switch (state) { case Constructing: case Constructed: case Configured: break; case Initialized: throwException("Still initialized"); break; default: throwUnknownState(); break; } } // CS_IGNORE_NEXT This is the only finalize() @Override protected final void finalize() throws Throwable { // CS_IGNORE try { ensureNoLongerInitialized(); } finally { super.finalize(); } } @Override public String toString() { // CS_IGNORE_PREV keep overridable return String.format("%s <%s>", getClass().getSimpleName(), getState()); } }