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.netconf.cli.reader.impl;
10 import static org.opendaylight.netconf.cli.io.IOUtil.isSkipInput;
11 import static org.opendaylight.netconf.cli.io.IOUtil.listType;
12 import com.google.common.base.Function;
13 import com.google.common.base.Optional;
14 import com.google.common.collect.BiMap;
15 import com.google.common.collect.Collections2;
16 import com.google.common.collect.HashBiMap;
17 import java.io.IOException;
18 import java.util.Collections;
19 import java.util.List;
20 import jline.console.completer.Completer;
21 import jline.console.completer.StringsCompleter;
22 import org.opendaylight.netconf.cli.io.ConsoleIO;
23 import org.opendaylight.netconf.cli.io.IOUtil;
24 import org.opendaylight.netconf.cli.reader.AbstractReader;
25 import org.opendaylight.netconf.cli.reader.ReadingException;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
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<NormalizedNode<?, ?>> 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<NormalizedNode<?, ?>> 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<NormalizedNode<?, ?>> wrapValue(final T schemaNode, final Object value) {
120 final NormalizedNode<?, ?> newNode = ImmutableLeafNodeBuilder.create()
121 .withNodeIdentifier(new NodeIdentifier(schemaNode.getQName()))
122 .withValue(value).build();
123 return Collections.<NormalizedNode<?, ?>>singletonList(newNode);
126 protected abstract TypeDefinition<?> getType(final T schemaNode);
128 protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
129 final TypeDefinition<?> type = getType(schemaNode);
130 final DataHolderCompleter currentCompleter;
132 // Add enum completer
133 if (type instanceof EnumTypeDefinition) {
134 currentCompleter = new EnumDataHolderCompleter(type);
135 } else if (type instanceof IdentityrefTypeDefinition) {
136 currentCompleter = new IdentityRefDataHolderCompleter(type, getSchemaContext());
138 currentCompleter = new GeneralDataHolderCompleter(type);
140 this.currentCompleter = currentCompleter;
141 return currentCompleter;
144 private static interface DataHolderCompleter extends Completer {
146 Object resolveValue(String rawValue) throws ReadingException;
149 private static class GeneralDataHolderCompleter implements DataHolderCompleter {
151 private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
152 private final TypeDefinition<?> type;
154 public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
156 codec = getCodecForType(type);
159 protected TypeDefinition<?> getType() {
163 private static Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
164 final TypeDefinition<?> type) {
167 .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
170 return Optional.absent();
174 public Object resolveValue(final String rawValue) throws ReadingException {
176 return codec.isPresent() ? codec.get().deserialize(rawValue) : rawValue;
177 } catch (final RuntimeException e) {
178 final String message = "It wasn't possible deserialize value " + rawValue + ".";
179 LOG.error(message, e);
180 throw new ReadingException(message, e);
185 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
190 private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
192 public EnumDataHolderCompleter(final TypeDefinition<?> type) {
197 public Object resolveValue(final String rawValue) throws ReadingException {
198 return super.resolveValue(rawValue);
202 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
203 return new StringsCompleter(Collections2.transform(((EnumTypeDefinition) getType()).getValues(),
204 new Function<EnumPair, String>() {
206 public String apply(final EnumPair input) {
207 return input.getName();
209 })).complete(buffer, cursor, candidates);
213 private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
215 private final BiMap<String, QName> identityMap;
217 public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
219 this.identityMap = getIdentityMap(schemaContext);
222 private static BiMap<String, QName> getIdentityMap(final SchemaContext schemaContext) {
223 final BiMap<String, QName> identityMap = HashBiMap.create();
224 for (final Module module : schemaContext.getModules()) {
225 for (final IdentitySchemaNode identity : module.getIdentities()) {
226 identityMap.put(getIdentityName(identity, module), identity.getQName());
232 private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
233 return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
237 public Object resolveValue(final String rawValue) throws ReadingException {
238 final QName qName = identityMap.get(rawValue);
240 throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
246 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
248 return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
249 .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
251 public String apply(final IdentitySchemaNode input) {
252 return identityMap.inverse().get(input.getQName());
254 })).complete(buffer, cursor, candidates);