Simplify boolean expressions
[netconf.git] / netconf / tools / netconf-cli / src / main / java / org / opendaylight / netconf / cli / reader / custom / ConfigReader.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.custom;
9
10 import static org.opendaylight.netconf.cli.io.IOUtil.isSkipInput;
11
12 import com.google.common.base.Function;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.base.Strings;
16 import com.google.common.collect.Collections2;
17 import com.google.common.collect.Lists;
18 import com.google.common.collect.Maps;
19 import java.io.IOException;
20 import java.net.URI;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.SortedSet;
26 import java.util.TreeSet;
27 import jline.console.completer.Completer;
28 import org.opendaylight.netconf.cli.CommandArgHandlerRegistry;
29 import org.opendaylight.netconf.cli.io.BaseConsoleContext;
30 import org.opendaylight.netconf.cli.io.ConsoleContext;
31 import org.opendaylight.netconf.cli.io.ConsoleIO;
32 import org.opendaylight.netconf.cli.io.IOUtil;
33 import org.opendaylight.netconf.cli.reader.AbstractReader;
34 import org.opendaylight.netconf.cli.reader.ReadingException;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
42 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.Module;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46
47 /**
48  * Custom reader implementation for filter elements in get/get-config rpcs. This
49  * reader overrides the default anyxml reader and reads filter as a schema path.
50  */
51 public class ConfigReader extends AbstractReader<DataSchemaNode> {
52
53     public static final String SEPARATOR = "/";
54
55     private final CommandArgHandlerRegistry commandArgHandlerRegistry;
56     private final Map<String, QName> mappedModules;
57     private final Map<URI, QName> mappedModulesNamespace;
58
59     public ConfigReader(final ConsoleIO console, final SchemaContext remoteSchemaContext,
60             final CommandArgHandlerRegistry commandArgHandlerRegistry) {
61         super(console, remoteSchemaContext);
62         this.commandArgHandlerRegistry = commandArgHandlerRegistry;
63
64         mappedModules = Maps.newHashMap();
65         mappedModulesNamespace = Maps.newHashMap();
66         for (final Module module : remoteSchemaContext.getModules()) {
67             final QName moduleQName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
68             mappedModules.put(moduleQName.getLocalName(), moduleQName);
69             mappedModulesNamespace.put(moduleQName.getNamespace(), moduleQName);
70         }
71     }
72
73     // FIXME refactor + unite common code with FilterReader
74
75     @Override
76     protected List<NormalizedNode<?, ?>> readWithContext(final DataSchemaNode schemaNode)
77             throws IOException, ReadingException {
78         console.writeLn("Config " + schemaNode.getQName().getLocalName());
79         console.writeLn("Submit path of the data to edit. Use TAB for autocomplete");
80
81         final String rawValue = console.read();
82
83         // FIXME isSkip check should be somewhere in abstractReader
84         if (isSkipInput(rawValue) || Strings.isNullOrEmpty(rawValue)) {
85             return Collections.emptyList();
86         }
87
88         final List<QName> filterPartsQNames = Lists.newArrayList();
89
90         for (final String part : rawValue.split(SEPARATOR)) {
91             final QName qName = IOUtil.qNameFromKeyString(part, mappedModules);
92             filterPartsQNames.add(qName);
93         }
94
95         List<? extends NormalizedNode<?, ?>> previous = readInnerNode(rawValue);
96
97         for (final QName qualifiedName : Lists.reverse(filterPartsQNames).subList(1, filterPartsQNames.size())) {
98             previous = Collections.<NormalizedNode<?, ?>>singletonList(
99                     ImmutableContainerNodeBuilder.create()
100                             .withNodeIdentifier(new NodeIdentifier(qualifiedName))
101                             .withValue(previous == null ? Collections.emptyList() : (Collection) previous).build()
102             );
103         }
104
105         if (previous == null) {
106             return Collections.singletonList(null);
107         }
108
109         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = ImmutableContainerNodeBuilder
110             .create();
111         builder.withNodeIdentifier(new NodeIdentifier(schemaNode.getQName()));
112         builder.withValue((Collection<DataContainerChild<?, ?>>) previous);
113
114         return Collections.singletonList(builder.build());
115     }
116
117     private List<NormalizedNode<?, ?>> readInnerNode(final String pathString) throws ReadingException {
118         final Optional<DataSchemaNode> schema = getCurrentNode(getSchemaContext(), pathString);
119         Preconditions.checkState(schema.isPresent(), "Unable to find schema for %s", pathString);
120         return commandArgHandlerRegistry.getGenericReader(getSchemaContext(), true).read(schema.get());
121     }
122
123     @Override
124     protected ConsoleContext getContext(final DataSchemaNode schemaNode) {
125         return new FilterConsoleContext(schemaNode, getSchemaContext());
126     }
127
128     private final class FilterConsoleContext extends BaseConsoleContext<DataSchemaNode> {
129
130         private final SchemaContext remoteSchemaContext;
131
132         FilterConsoleContext(final DataSchemaNode schemaNode, final SchemaContext remoteSchemaContext) {
133             super(schemaNode);
134             this.remoteSchemaContext = remoteSchemaContext;
135         }
136
137         @Override
138         protected List<Completer> getAdditionalCompleters() {
139             return Collections.singletonList(new FilterCompleter(remoteSchemaContext));
140         }
141     }
142
143     private final class FilterCompleter implements Completer {
144
145         private final SchemaContext remoteSchemaContext;
146
147         FilterCompleter(final SchemaContext remoteSchemaContext) {
148             this.remoteSchemaContext = remoteSchemaContext;
149         }
150
151         @Override
152         public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
153             final int idx = buffer.lastIndexOf(SEPARATOR);
154
155             final Optional<DataSchemaNode> currentNode = getCurrentNode(remoteSchemaContext, buffer);
156             if (currentNode.isPresent() && currentNode.get() instanceof DataNodeContainer) {
157                 final Collection<DataSchemaNode> childNodes = ((DataNodeContainer) currentNode.get()).getChildNodes();
158                 final Collection<String> transformed = Collections2.transform(childNodes,
159                         new Function<DataSchemaNode, String>() {
160                             @Override
161                             public String apply(final DataSchemaNode input) {
162                                 return IOUtil.qNameToKeyString(input.getQName(),
163                                         mappedModulesNamespace.get(input.getQName().getNamespace()).getLocalName());
164                             }
165                         });
166
167                 fillCandidates(buffer.substring(idx + 1), candidates, transformed);
168             }
169
170             return idx == -1 ? 0 : idx + 1;
171         }
172
173         private void fillCandidates(final String buffer, final List<CharSequence> candidates,
174                 final Collection<String> transformed) {
175             final SortedSet<String> strings = new TreeSet<>(transformed);
176
177             if (buffer == null) {
178                 candidates.addAll(strings);
179             } else {
180                 for (final String match : strings.tailSet(buffer)) {
181                     if (!match.startsWith(buffer)) {
182                         break;
183                     }
184                     candidates.add(match);
185                 }
186             }
187
188             if (candidates.size() == 1) {
189                 candidates.set(0, candidates.get(0) + SEPARATOR);
190             }
191         }
192
193     }
194
195     private Optional<DataSchemaNode> getCurrentNode(DataSchemaNode parent, final String buffer) {
196         for (final String part : buffer.split(SEPARATOR)) {
197             if (!IOUtil.isQName(part)) {
198                 return Optional.of(parent);
199             }
200
201             final QName qName;
202             try {
203                 qName = IOUtil.qNameFromKeyString(part, mappedModules);
204             } catch (final ReadingException e) {
205                 return Optional.of(parent);
206             }
207             if (parent instanceof DataNodeContainer) {
208                 parent = ((DataNodeContainer) parent).getDataChildByName(qName);
209             } else {
210                 // This should check if we are at the end of buffer ?
211                 return Optional.of(parent);
212             }
213         }
214         return Optional.of(parent);
215     }
216
217 }