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.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.Module;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
37 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
38 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
39 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
40 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 public abstract class BasicDataHolderReader<T extends DataSchemaNode> extends AbstractReader<T> {
46 private static final Logger LOG = LoggerFactory.getLogger(BasicDataHolderReader.class);
47 private DataHolderCompleter currentCompleter;
49 public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext,
50 final boolean readConfigNode) {
51 super(console, schemaContext, readConfigNode);
54 public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext) {
55 super(console, schemaContext);
59 public List<NormalizedNode<?, ?>> readWithContext(final T schemaNode) throws IOException, ReadingException {
60 TypeDefinition<?> type = getType(schemaNode);
61 console.formatLn("Submit %s %s(%s)", listType(schemaNode), schemaNode.getQName().getLocalName(), type.getQName().getLocalName());
63 while (baseTypeFor(type) instanceof UnionTypeDefinition) {
64 final Optional<TypeDefinition<?>> optionalTypeDef = new UnionTypeReader(console).read(type);
65 if (!optionalTypeDef.isPresent()) {
66 return postSkipOperations(schemaNode);
68 type = optionalTypeDef.get();
71 if (currentCompleter == null) {
72 currentCompleter = getBaseCompleter(schemaNode);
75 // TODO what if type is leafref, instance-identifier?
77 // Handle empty type leaf by question
78 if (isEmptyType(type)) {
79 final Optional<Boolean> shouldAddEmpty = new DecisionReader().read(console, "Add empty type leaf %s ?",
80 schemaNode.getQName().getLocalName());
81 if (shouldAddEmpty.isPresent()) {
82 if (shouldAddEmpty.get()) {
83 return wrapValue(schemaNode, "");
85 return Collections.emptyList();
88 return postSkipOperations(schemaNode);
92 final String rawValue = readValue();
93 if (isSkipInput(rawValue)) {
94 return postSkipOperations(schemaNode);
97 final Object resolvedValue = currentCompleter.resolveValue(rawValue);
99 // Reset state TODO should be in finally
100 currentCompleter = null;
101 return wrapValue(schemaNode, resolvedValue);
104 private List<NormalizedNode<?, ?>> postSkipOperations(final DataSchemaNode schemaNode) throws IOException {
105 console.formatLn("Skipping %s", schemaNode.getQName());
106 return Collections.emptyList();
109 private TypeDefinition<?> baseTypeFor(final TypeDefinition<?> type) {
110 if (type.getBaseType() != null) {
111 return baseTypeFor(type.getBaseType());
116 protected String readValue() throws IOException {
117 return console.read();
120 private List<NormalizedNode<?, ?>> wrapValue(final T schemaNode, final Object value) {
121 final NormalizedNode<?, ?> newNode = ImmutableLeafNodeBuilder.create()
122 .withNodeIdentifier(new NodeIdentifier(schemaNode.getQName()))
123 .withValue(value).build();
124 return Collections.<NormalizedNode<?, ?>>singletonList(newNode);
127 protected abstract TypeDefinition<?> getType(final T schemaNode);
129 protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
130 final TypeDefinition<?> type = getType(schemaNode);
131 final DataHolderCompleter currentCompleter;
133 // Add enum completer
134 if (type instanceof EnumTypeDefinition) {
135 currentCompleter = new EnumDataHolderCompleter(type);
136 } else if (type instanceof IdentityrefTypeDefinition) {
137 currentCompleter = new IdentityRefDataHolderCompleter(type, getSchemaContext());
139 currentCompleter = new GeneralDataHolderCompleter(type);
141 this.currentCompleter = currentCompleter;
142 return currentCompleter;
145 private static interface DataHolderCompleter extends Completer {
147 Object resolveValue(String rawValue) throws ReadingException;
150 private static class GeneralDataHolderCompleter implements DataHolderCompleter {
152 private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
153 private final TypeDefinition<?> type;
155 public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
157 codec = getCodecForType(type);
160 protected TypeDefinition<?> getType() {
164 private Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
165 final TypeDefinition<?> type) {
168 .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
171 return Optional.absent();
175 public Object resolveValue(final String rawValue) throws ReadingException {
177 return codec.isPresent() ? codec.get().deserialize(rawValue) : rawValue;
178 } catch (final RuntimeException e) {
179 final String message = "It wasn't possible deserialize value " + rawValue + ".";
180 LOG.error(message, e);
181 throw new ReadingException(message, e);
186 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
191 private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
193 public EnumDataHolderCompleter(final TypeDefinition<?> type) {
198 public Object resolveValue(final String rawValue) throws ReadingException {
199 return super.resolveValue(rawValue);
203 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
204 return new StringsCompleter(Collections2.transform(((EnumTypeDefinition) getType()).getValues(),
205 new Function<EnumPair, String>() {
207 public String apply(final EnumPair input) {
208 return input.getName();
210 })).complete(buffer, cursor, candidates);
214 private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
216 private final BiMap<String, QName> identityMap;
218 public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
220 this.identityMap = getIdentityMap(schemaContext);
223 private static BiMap<String, QName> getIdentityMap(final SchemaContext schemaContext) {
224 final BiMap<String, QName> identityMap = HashBiMap.create();
225 for (final Module module : schemaContext.getModules()) {
226 for (final IdentitySchemaNode identity : module.getIdentities()) {
227 identityMap.put(getIdentityName(identity, module), identity.getQName());
233 private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
234 return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
238 public Object resolveValue(final String rawValue) throws ReadingException {
239 final QName qName = identityMap.get(rawValue);
241 throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
247 public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
249 return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
250 .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
252 public String apply(final IdentitySchemaNode input) {
253 return identityMap.inverse().get(input.getQName());
255 })).complete(buffer, cursor, candidates);