Simplify boolean expressions
[netconf.git] / netconf / tools / netconf-cli / src / main / java / org / opendaylight / netconf / cli / Cli.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.cli;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.io.IOException;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import jline.console.UserInterruptException;
17 import jline.console.completer.Completer;
18 import jline.console.completer.StringsCompleter;
19 import org.opendaylight.netconf.cli.commands.Command;
20 import org.opendaylight.netconf.cli.commands.CommandConstants;
21 import org.opendaylight.netconf.cli.commands.CommandDispatcher;
22 import org.opendaylight.netconf.cli.commands.CommandInvocationException;
23 import org.opendaylight.netconf.cli.commands.input.Input;
24 import org.opendaylight.netconf.cli.commands.input.InputDefinition;
25 import org.opendaylight.netconf.cli.commands.output.Output;
26 import org.opendaylight.netconf.cli.commands.output.OutputDefinition;
27 import org.opendaylight.netconf.cli.io.ConsoleContext;
28 import org.opendaylight.netconf.cli.io.ConsoleIO;
29 import org.opendaylight.netconf.cli.reader.ReadingException;
30 import org.opendaylight.netconf.cli.writer.OutFormatter;
31 import org.opendaylight.netconf.cli.writer.WriteException;
32 import org.opendaylight.netconf.cli.writer.Writer;
33 import org.opendaylight.netconf.cli.writer.impl.NormalizedNodeWriter;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
38
39 /**
40  * The top level cli state that dispatches command executions.
41  */
42 public class Cli implements Runnable {
43     private final CommandDispatcher commandRegistry;
44     private final CommandArgHandlerRegistry argumentHandlerRegistry;
45     private final SchemaContextRegistry schemaContextRegistry;
46     private final ConsoleIO consoleIO;
47
48     public Cli(final ConsoleIO consoleIO, final CommandDispatcher commandRegistry,
49            final CommandArgHandlerRegistry argumentHandlerRegistry,
50            final SchemaContextRegistry schemaContextRegistry) {
51         this.consoleIO = consoleIO;
52         this.commandRegistry = commandRegistry;
53         this.argumentHandlerRegistry = argumentHandlerRegistry;
54         this.schemaContextRegistry = schemaContextRegistry;
55     }
56
57     @Override
58     public void run() {
59         try {
60             consoleIO.writeLn("Cli is up, available commands:");
61             final RootConsoleContext consoleContext = new RootConsoleContext(commandRegistry);
62             consoleIO.enterContext(consoleContext);
63             consoleIO.complete();
64             consoleIO.writeLn("");
65
66             while (true) {
67                 final String commandName = consoleIO.read();
68                 final Optional<Command> commandOpt = commandRegistry.getCommand(commandName);
69
70                 if (!commandOpt.isPresent()) {
71                     continue;
72                 }
73
74                 final Command command = commandOpt.get();
75                 try {
76                     consoleIO.enterContext(command.getConsoleContext());
77                     final Output response = command.invoke(handleInput(command.getInputDefinition()));
78                     handleOutput(command, response);
79                 } catch (final CommandInvocationException e) {
80                     consoleIO.write(e.getMessage());
81                 } catch (final UserInterruptException e) {
82                     consoleIO.writeLn("Command " + command.getCommandId() + " was terminated.");
83                 } finally {
84                     consoleIO.leaveContext();
85                 }
86
87             }
88         } catch (final IOException e) {
89             throw new RuntimeException("IO failure", e);
90         }
91     }
92
93     private void handleOutput(final Command command, final Output response) {
94         final OutputDefinition outputDefinition = command.getOutputDefinition();
95
96         final Writer<DataSchemaNode> outHandler = argumentHandlerRegistry.getGenericWriter();
97         if (outputDefinition.isEmpty()) {
98             handleEmptyOutput(command, response);
99         } else {
100             handleRegularOutput(response, outputDefinition, outHandler);
101         }
102     }
103
104     private void handleRegularOutput(final Output response, final OutputDefinition outputDefinition,
105             final Writer<DataSchemaNode> outHandler) {
106         final Map<DataSchemaNode, List<NormalizedNode<?, ?>>> unwrap = response.unwrap(outputDefinition);
107
108         for (final DataSchemaNode schemaNode : unwrap.keySet()) {
109             Preconditions.checkNotNull(schemaNode);
110
111             try {
112
113                 // FIXME move custom writer to GenericWriter/Serializers ...
114                 // this checks only first level
115                 final Optional<Class<? extends Writer<DataSchemaNode>>> customReaderClassOpt = tryGetCustomHandler(
116                     schemaNode);
117
118                 if (customReaderClassOpt.isPresent()) {
119                     final Writer<DataSchemaNode> customReaderInstance = argumentHandlerRegistry
120                             .getCustomWriter(customReaderClassOpt.get());
121                     Preconditions.checkNotNull(customReaderInstance, "Unknown custom writer: %s",
122                             customReaderClassOpt.get());
123                     customReaderInstance.write(schemaNode, unwrap.get(schemaNode));
124                 } else {
125                     outHandler.write(schemaNode, unwrap.get(schemaNode));
126                 }
127
128             } catch (final WriteException e) {
129                 throw new IllegalStateException("Unable to write value for: " + schemaNode.getQName() + " from: "
130                         + unwrap.get(schemaNode), e);
131             }
132         }
133     }
134
135     private void handleEmptyOutput(final Command command, final Output response) {
136         try {
137             new NormalizedNodeWriter(consoleIO, new OutFormatter()).write(null,
138                     Collections.<NormalizedNode<?, ?>>singletonList(response.getOutput()));
139         } catch (final WriteException e) {
140             throw new IllegalStateException("Unable to write value for: " + response.getOutput().getNodeType()
141                     + " from: " + command.getCommandId(), e);
142         }
143     }
144
145     private Input handleInput(final InputDefinition inputDefinition) {
146         List<NormalizedNode<?, ?>> allArgs = Collections.emptyList();
147         try {
148             if (!inputDefinition.isEmpty()) {
149                 allArgs = argumentHandlerRegistry.getGenericReader(schemaContextRegistry.getLocalSchemaContext()).read(
150                         inputDefinition.getInput());
151             }
152         } catch (final ReadingException e) {
153             throw new IllegalStateException("Unable to read value for: " + inputDefinition.getInput().getQName(), e);
154         }
155
156         return new Input(allArgs);
157     }
158
159     // TODO move tryGet to GenericWriter, GenericReader has the same code
160     private <T> Optional<Class<? extends T>> tryGetCustomHandler(final DataSchemaNode dataSchemaNode) {
161
162         for (final UnknownSchemaNode unknownSchemaNode : dataSchemaNode.getUnknownSchemaNodes()) {
163
164             if (isExtenstionForCustomHandler(unknownSchemaNode)) {
165                 final String argumentHandlerClassName = unknownSchemaNode.getNodeParameter();
166                 try {
167                     final Class<?> argumentClass = Class.forName(argumentHandlerClassName);
168                     // TODO add check before cast
169                     return Optional.of((Class<? extends T>) argumentClass);
170                 } catch (final ClassNotFoundException e) {
171                     throw new IllegalArgumentException("Unknown custom reader class " + argumentHandlerClassName
172                             + " for: " + dataSchemaNode.getQName());
173                 }
174             }
175         }
176
177         return Optional.absent();
178     }
179
180     private boolean isExtenstionForCustomHandler(final UnknownSchemaNode unknownSchemaNode) {
181         final QName qName = unknownSchemaNode.getExtensionDefinition().getQName();
182         return qName.equals(CommandConstants.ARG_HANDLER_EXT_QNAME);
183     }
184
185     private static final class RootConsoleContext implements ConsoleContext {
186
187         private final Completer completer;
188
189         RootConsoleContext(final CommandDispatcher commandRegistry) {
190             completer = new CommandCompleter(commandRegistry);
191         }
192
193         @Override
194         public Completer getCompleter() {
195             return completer;
196         }
197
198         @Override
199         public Optional<String> getPrompt() {
200             return Optional.absent();
201         }
202
203         private class CommandCompleter extends StringsCompleter {
204
205             private final CommandDispatcher commandRegistry;
206
207             CommandCompleter(final CommandDispatcher commandRegistry) {
208                 this.commandRegistry = commandRegistry;
209             }
210
211             @Override
212             public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
213                 getStrings().clear();
214                 getStrings().addAll(commandRegistry.getCommandIds());
215                 return super.complete(buffer, cursor, candidates);
216             }
217         }
218     }
219
220 }