001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2018 nobark (tools4j), Marco Terzer, Anton Anufriev 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.tools4j.nobark.loop; 025 026import java.util.Objects; 027 028/** 029 * Extension of {@link Step} with enhanced functionality facilitating the composition of step chains. 030 */ 031@FunctionalInterface 032public interface ComposableStep extends Step { 033 034 /** 035 * Returns a composable step that performs first <i><tt>this</tt></i> and then the <i><tt>next</tt></i> step. 036 * <p> 037 * The procedure eliminates {@link #NO_OP no-OP} steps, i.e. it returns <i><tt>this</tt></i> if 038 * <i><tt>(next == NO_OP)</tt></i>. 039 * 040 * @param next the next step to be performed after <i><tt>this</tt></i> 041 * @return a composite step performing <i><tt>this</tt></i> and then <i><tt>next</tt></i> 042 */ 043 default ComposableStep then(final Step next) { 044 Objects.requireNonNull(next); 045 return next == NO_OP ? this : () -> perform() | next.perform(); 046 } 047 048 /** 049 * Returns a composable step that performs first <i><tt>this</tt></i> step and then the <i><tt>next</tt></i> step 050 * only if the first performed any work as indicated by result of the {@link #perform()} invocation. 051 * <p> 052 * The procedure eliminates {@link #NO_OP no-OP} steps, i.e. it returns <i><tt>this</tt></i> if 053 * <i><tt>(next == NO_OP)</tt></i>. 054 * 055 * @param next the next step to be performed after <i><tt>this</tt></i> only if it performed some work 056 * @return a composite step performing <i><tt>this</tt></i>, and if some work was performed subsequently also 057 * <i><tt>next</tt></i> 058 */ 059 default ComposableStep thenIfPerformed(final Step next) { 060 Objects.requireNonNull(next); 061 return next == NO_OP ? this : () -> perform() && (true | next.perform());//NOTE: true is needed for correctness 062 } 063 064 /** 065 * Returns a composable step that performs first <i><tt>this</tt></i> step and then the <i><tt>next</tt></i> step 066 * only if the first performed no work as indicated by result of the {@link #perform()} invocation. 067 * <p> 068 * The procedure eliminates {@link #NO_OP no-OP} steps, i.e. it returns <i><tt>this</tt></i> if 069 * <i><tt>(next == NO_OP)</tt></i>. 070 * 071 * @param next the next step to be performed after <i><tt>this</tt></i> only if it performed no work 072 * @return a composite step performing <i><tt>this</tt></i>, and if no work was performed subsequently 073 * <i><tt>next</tt></i> 074 */ 075 default ComposableStep thenIfNotPerformed(final Step next) { 076 Objects.requireNonNull(next); 077 return next == NO_OP ? this : () -> perform() || next.perform(); 078 } 079 080 /** 081 * Returns a composable step given a "normal" step. 082 * <p> 083 * The procedure preserves {@link #NO_OP no-OP} steps as well as existing instances of {@code ComposableStep}, 084 * which means that such values are returned unchanged. 085 * 086 * @param step the step to be wrapped as a {@code ComposableStep} 087 * @return a {@code ComposableStep} doing exactly the same work as <i><tt>step</tt></i> but with a richer interface 088 * for step coposition 089 */ 090 static ComposableStep create(final Step step) { 091 Objects.requireNonNull(step); 092 return step == NO_OP ? NO_OP : (step instanceof ComposableStep ? (ComposableStep)step : step::perform); 093 } 094 095 /** 096 * Returns a step that runs all given component steps; the {@link #perform()} method of the resulting composite 097 * step returns true if any of the component steps returns true. The procedure uses {@link #then(Step)} for the 098 * composition and eliminates {@link #NO_OP no-OP} components. 099 * 100 * @param components the component steps forming the parts of the returned step 101 * @return a new step that performs all component steps 102 * @see #NO_OP 103 */ 104 static ComposableStep composite(final Step... components) { 105 Objects.requireNonNull(components); 106 ComposableStep result = NO_OP; 107 for (final Step component : components) { 108 result = result.then(component); 109 } 110 return result; 111 } 112 113 /** 114 * Step performing a no-OP; the implementation returns false indicating that no work was performed. All composition 115 * methods are optimised to eliminate no-OP steps. 116 */ 117 ComposableStep NO_OP = new ComposableStep() { 118 @Override 119 public boolean perform() { 120 return false; 121 } 122 123 @Override 124 public ComposableStep then(final Step next) { 125 return ComposableStep.create(next); 126 } 127 128 @Override 129 public ComposableStep thenIfPerformed(final Step next) { 130 Objects.requireNonNull(next); 131 return NO_OP; 132 } 133 134 @Override 135 public ComposableStep thenIfNotPerformed(final Step next) { 136 return ComposableStep.create(next); 137 } 138 }; 139 140}