Fix netconf-connector-config groupId
[netconf.git] / netconf / tools / netconf-cli / src / main / java / org / opendaylight / netconf / cli / reader / impl / BasicDataHolderReader.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.reader.impl;
9
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;
42
43 public abstract class BasicDataHolderReader<T extends DataSchemaNode> extends AbstractReader<T> {
44
45     private static final Logger LOG = LoggerFactory.getLogger(BasicDataHolderReader.class);
46     private DataHolderCompleter currentCompleter;
47
48     public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext,
49             final boolean readConfigNode) {
50         super(console, schemaContext, readConfigNode);
51     }
52
53     public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext) {
54         super(console, schemaContext);
55     }
56
57     @Override
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());
61
62         while (baseTypeFor(type) instanceof UnionTypeDefinition) {
63             final Optional<TypeDefinition<?>> optionalTypeDef = new UnionTypeReader(console).read(type);
64             if (!optionalTypeDef.isPresent()) {
65                 return postSkipOperations(schemaNode);
66             }
67             type = optionalTypeDef.get();
68         }
69
70         if (currentCompleter == null) {
71             currentCompleter = getBaseCompleter(schemaNode);
72         }
73
74         // TODO what if type is leafref, instance-identifier?
75
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, "");
83                 } else {
84                     return Collections.emptyList();
85                 }
86             } else {
87                 return postSkipOperations(schemaNode);
88             }
89         }
90
91         final String rawValue = readValue();
92         if (isSkipInput(rawValue)) {
93             return postSkipOperations(schemaNode);
94         }
95
96         final Object resolvedValue = currentCompleter.resolveValue(rawValue);
97
98         // Reset state TODO should be in finally
99         currentCompleter = null;
100         return wrapValue(schemaNode, resolvedValue);
101     }
102
103     private List<NormalizedNode<?, ?>> postSkipOperations(final DataSchemaNode schemaNode) throws IOException {
104         console.formatLn("Skipping %s", schemaNode.getQName());
105         return Collections.emptyList();
106     }
107
108     private TypeDefinition<?> baseTypeFor(final TypeDefinition<?> type) {
109         if (type.getBaseType() != null) {
110             return baseTypeFor(type.getBaseType());
111         }
112         return type;
113     }
114
115     protected String readValue() throws IOException {
116         return console.read();
117     }
118
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);
124     }
125
126     protected abstract TypeDefinition<?> getType(final T schemaNode);
127
128     protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
129         final TypeDefinition<?> type = getType(schemaNode);
130         final DataHolderCompleter currentCompleter;
131
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());
137         } else {
138             currentCompleter = new GeneralDataHolderCompleter(type);
139         }
140         this.currentCompleter = currentCompleter;
141         return currentCompleter;
142     }
143
144     private static interface DataHolderCompleter extends Completer {
145
146         Object resolveValue(String rawValue) throws ReadingException;
147     }
148
149     private static class GeneralDataHolderCompleter implements DataHolderCompleter {
150
151         private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
152         private final TypeDefinition<?> type;
153
154         public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
155             this.type = type;
156             codec = getCodecForType(type);
157         }
158
159         protected TypeDefinition<?> getType() {
160             return type;
161         }
162
163         private static Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
164                 final TypeDefinition<?> type) {
165             if (type != null) {
166                 return Optional
167                         .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
168                                 .from(type));
169             }
170             return Optional.absent();
171         }
172
173         @Override
174         public Object resolveValue(final String rawValue) throws ReadingException {
175             try {
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);
181             }
182         }
183
184         @Override
185         public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
186             return 0;
187         }
188     }
189
190     private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
191
192         public EnumDataHolderCompleter(final TypeDefinition<?> type) {
193             super(type);
194         }
195
196         @Override
197         public Object resolveValue(final String rawValue) throws ReadingException {
198             return super.resolveValue(rawValue);
199         }
200
201         @Override
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>() {
205                         @Override
206                         public String apply(final EnumPair input) {
207                             return input.getName();
208                         }
209                     })).complete(buffer, cursor, candidates);
210         }
211     }
212
213     private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
214
215         private final BiMap<String, QName> identityMap;
216
217         public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
218             super(type);
219             this.identityMap = getIdentityMap(schemaContext);
220         }
221
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());
227                 }
228             }
229             return identityMap;
230         }
231
232         private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
233             return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
234         }
235
236         @Override
237         public Object resolveValue(final String rawValue) throws ReadingException {
238             final QName qName = identityMap.get(rawValue);
239             if (qName == null) {
240                 throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
241             }
242             return qName;
243         }
244
245         @Override
246         public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
247
248             return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
249                     .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
250                         @Override
251                         public String apply(final IdentitySchemaNode input) {
252                             return identityMap.inverse().get(input.getQName());
253                         }
254                     })).complete(buffer, cursor, candidates);
255         }
256     }
257 }