Merge "Distributed Datastore integration with config subsystem Updated with the usage...
[controller.git] / opendaylight / netconf / netconf-cli / src / main / java / org / opendaylight / controller / 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.controller.netconf.cli.reader.impl;
9
10 import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
11 import static org.opendaylight.controller.netconf.cli.io.IOUtil.listType;
12
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;
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<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());
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<Node<?>> 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<Node<?>> wrapValue(final T schemaNode, final Object value) {
120         final Node<?> newNode = NodeFactory.createImmutableSimpleNode(schemaNode.getQName(), null, value);
121         return Collections.<Node<?>> singletonList(newNode);
122     }
123
124     protected abstract TypeDefinition<?> getType(final T schemaNode);
125
126     protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
127         final TypeDefinition<?> type = getType(schemaNode);
128         final DataHolderCompleter currentCompleter;
129
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());
135         } else {
136             currentCompleter = new GeneralDataHolderCompleter(type);
137         }
138         this.currentCompleter = currentCompleter;
139         return currentCompleter;
140     }
141
142     private static interface DataHolderCompleter extends Completer {
143
144         Object resolveValue(String rawValue) throws ReadingException;
145     }
146
147     private static class GeneralDataHolderCompleter implements DataHolderCompleter {
148
149         private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
150         private final TypeDefinition<?> type;
151
152         public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
153             this.type = type;
154             codec = getCodecForType(type);
155         }
156
157         protected TypeDefinition<?> getType() {
158             return type;
159         }
160
161         private Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
162                 final TypeDefinition<?> type) {
163             if (type != null) {
164                 return Optional
165                         .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
166                                 .from(type));
167             }
168             return Optional.absent();
169         }
170
171         @Override
172         public Object resolveValue(final String rawValue) throws ReadingException {
173             try {
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);
179             }
180         }
181
182         @Override
183         public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
184             return 0;
185         }
186     }
187
188     private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
189
190         public EnumDataHolderCompleter(final TypeDefinition<?> type) {
191             super(type);
192         }
193
194         @Override
195         public Object resolveValue(final String rawValue) throws ReadingException {
196             return super.resolveValue(rawValue);
197         }
198
199         @Override
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>() {
203                         @Override
204                         public String apply(final EnumPair input) {
205                             return input.getName();
206                         }
207                     })).complete(buffer, cursor, candidates);
208         }
209     }
210
211     private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
212
213         private final BiMap<String, QName> identityMap;
214
215         public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
216             super(type);
217             this.identityMap = getIdentityMap(schemaContext);
218         }
219
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());
225                 }
226             }
227             return identityMap;
228         }
229
230         private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
231             return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
232         }
233
234         @Override
235         public Object resolveValue(final String rawValue) throws ReadingException {
236             final QName qName = identityMap.get(rawValue);
237             if (qName == null) {
238                 throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
239             }
240             return qName;
241         }
242
243         @Override
244         public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
245
246             return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
247                     .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
248                 @Override
249                 public String apply(final IdentitySchemaNode input) {
250                     return identityMap.inverse().get(input.getQName());
251                 }
252             })).complete(buffer, cursor, candidates);
253         }
254     }
255 }