2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.netconf.cli.reader.impl;
10 import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
11 import static org.opendaylight.controller.netconf.cli.io.IOUtil.listType;
13 import com.google.common.base.Function;
14 import com.google.common.base.Optional;
15 import com.google.common.collect.BiMap;
16 import com.google.common.collect.Collections2;
17 import com.google.common.collect.HashBiMap;
18 import java.io.IOException;
19 import java.util.Collections;
20 import java.util.List;
21 import jline.console.completer.Completer;
22 import jline.console.completer.StringsCompleter;
23 import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
24 import org.opendaylight.controller.netconf.cli.io.IOUtil;
25 import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
26 import org.opendaylight.controller.netconf.cli.reader.ReadingException;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.Node;
29 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
30 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.Module;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
37 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
38 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public abstract class BasicDataHolderReader<T extends DataSchemaNode> extends AbstractReader<T> {
45 private static final Logger LOG = LoggerFactory.getLogger(BasicDataHolderReader.class);
46 private DataHolderCompleter currentCompleter;
48 public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext,
49 final boolean readConfigNode) {
50 super(console, schemaContext, readConfigNode);
53 public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext) {
54 super(console, schemaContext);
58 public List<Node<?>> readWithContext(final T schemaNode) throws IOException, ReadingException {
59 TypeDefinition<?> type = getType(schemaNode);
60 console.formatLn("Submit %s %s(%s)", listType(schemaNode), schemaNode.getQName().getLocalName(), type.getQName().getLocalName());
62 while (baseTypeFor(type) instanceof UnionTypeDefinition) {
63 final Optional<TypeDefinition<?>> optionalTypeDef = new UnionTypeReader(console).read(type);
64 if (!optionalTypeDef.isPresent()) {
65 return postSkipOperations(schemaNode);
67 type = optionalTypeDef.get();
70 if (currentCompleter == null) {
71 currentCompleter = getBaseCompleter(schemaNode);
74 // TODO what if type is leafref, instance-identifier?
76 // Handle empty type leaf by question
77 if (isEmptyType(type)) {
78 final Optional<Boolean> shouldAddEmpty = new DecisionReader().read(console, "Add empty type leaf %s ?",
79 schemaNode.getQName().getLocalName());
80 if (shouldAddEmpty.isPresent()) {
81 if (shouldAddEmpty.get()) {
82 return wrapValue(schemaNode, "");
84 return Collections.emptyList();
87 return postSkipOperations(schemaNode);
91 final String rawValue = readValue();
92 if (isSkipInput(rawValue)) {
93 return postSkipOperations(schemaNode);
96 final Object resolvedValue = currentCompleter.resolveValue(rawValue);
98 // Reset state TODO should be in finally
99 currentCompleter = null;
100 return wrapValue(schemaNode, resolvedValue);
103 private List<Node<?>> postSkipOperations(final DataSchemaNode schemaNode) throws IOException {
104 console.formatLn("Skipping %s", schemaNode.getQName());
105 return Collections.emptyList();
108 private TypeDefinition<?> baseTypeFor(final TypeDefinition<?> type) {
109 if (type.getBaseType() != null) {
110 return baseTypeFor(type.getBaseType());
115 protected String readValue() throws IOException {
116 return console.read();
119 private List<Node<?>> wrapValue(final T schemaNode, final Object value) {
120 final Node<?> newNode = NodeFactory.createImmutableSimpleNode(schemaNode.getQName(), null, value);
121 return Collections.<Node<?>> singletonList(newNode);
124 protected abstract TypeDefinition<?> getType(final T schemaNode);
126 protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
127 final TypeDefinition<?> type = getType(schemaNode);
128 final DataHolderCompleter currentCompleter;
130 // Add enum completer
131 if (type instanceof EnumTypeDefinition) {
132 currentCompleter = new EnumDataHolderCompleter(type);
133 } else if (type instanceof IdentityrefTypeDefinition) {
134 currentCompleter = new IdentityRefDataHolderCompleter(type, getSchemaContext());
136 currentCompleter = new GeneralDataHolderCompleter(type);
138 this.currentCompleter = currentCompleter;
139 return currentCompleter;
142 private static interface DataHolderCompleter extends Completer {
144 Object resolveValue(String rawValue) throws ReadingException;
147 private static class GeneralDataHolderCompleter implements DataHolderCompleter {
149 private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
150 private final TypeDefinition<?> type;
152 public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
154 codec = getCodecForType(type);
157 protected TypeDefinition<?> getType() {
161 private Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
162 final TypeDefinition<?> type) {
165 .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
168 return Optional.absent();
172 public Object resolveValue(final String rawValue) throws ReadingException {
174 return codec.isPresent() ? codec.get().deserialize(rawValue) : rawValue;
175 } catch (final RuntimeException e) {
176 final String message = "It wasn't possible deserialize value " + rawValue + ".";
177 LOG.error(message, e);
178 throw new ReadingException(message, e);
183 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
188 private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
190 public EnumDataHolderCompleter(final TypeDefinition<?> type) {
195 public Object resolveValue(final String rawValue) throws ReadingException {
196 return super.resolveValue(rawValue);
200 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
201 return new StringsCompleter(Collections2.transform(((EnumTypeDefinition) getType()).getValues(),
202 new Function<EnumPair, String>() {
204 public String apply(final EnumPair input) {
205 return input.getName();
207 })).complete(buffer, cursor, candidates);
211 private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
213 private final BiMap<String, QName> identityMap;
215 public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
217 this.identityMap = getIdentityMap(schemaContext);
220 private static BiMap<String, QName> getIdentityMap(final SchemaContext schemaContext) {
221 final BiMap<String, QName> identityMap = HashBiMap.create();
222 for (final Module module : schemaContext.getModules()) {
223 for (final IdentitySchemaNode identity : module.getIdentities()) {
224 identityMap.put(getIdentityName(identity, module), identity.getQName());
230 private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
231 return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
235 public Object resolveValue(final String rawValue) throws ReadingException {
236 final QName qName = identityMap.get(rawValue);
238 throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
244 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
246 return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
247 .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
249 public String apply(final IdentitySchemaNode input) {
250 return identityMap.inverse().get(input.getQName());
252 })).complete(buffer, cursor, candidates);