1 /* 2 * Copyright (C) 2019 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.example.atm; 18 19 import dagger.example.atm.Command.Result; 20 import dagger.example.atm.Command.Status; 21 import java.util.ArrayDeque; 22 import java.util.Deque; 23 import javax.inject.Inject; 24 import javax.inject.Singleton; 25 26 /** 27 * Processes successive commands by delegating to a {@link CommandRouter}. 28 * 29 * <p>Whereas {@link CommandRouter} routes an input string to a particular {@link Command}, this 30 * class maintains inter-command state to determine which {@link CommandRouter} should route 31 * successive commands. 32 * 33 * <p>This class is {@link Singleton} scoped because it has mutable state ({@code 34 * commandRouterStack}), and all users of {@link CommandProcessor} must use the same instance. 35 */ 36 @Singleton 37 final class CommandProcessor { 38 private final Deque<CommandRouter> commandRouterStack = new ArrayDeque<>(); 39 40 @Inject CommandProcessor(CommandRouter firstCommandRouter)41 CommandProcessor(CommandRouter firstCommandRouter) { 42 commandRouterStack.push(firstCommandRouter); 43 } 44 process(String input)45 Status process(String input) { 46 if (commandRouterStack.isEmpty()) { 47 throw new IllegalStateException("No command router is available!"); 48 } 49 50 Result result = commandRouterStack.peek().route(input); 51 switch (result.status()) { 52 case INPUT_COMPLETED: 53 commandRouterStack.pop(); 54 return commandRouterStack.isEmpty() ? Status.INPUT_COMPLETED : Status.HANDLED; 55 case HANDLED: 56 // TODO(ronshapiro): We currently have a case of using a subcomponent for nested commands, 57 // which requires maintaining a binding indicating whether we are in the subcomponent are 58 // not. We can include another example where there's a CommandRouter that is created from an 59 // entirely different component, that way there are no inherited commands. 60 result.nestedCommandRouter().ifPresent(commandRouterStack::push); 61 // fall through 62 case INVALID: 63 return result.status(); 64 } 65 throw new AssertionError(result.status()); 66 } 67 } 68