X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fnetconf-cli%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fcli%2Freader%2Fcustom%2FConfigReader.java;fp=opendaylight%2Fnetconf%2Fnetconf-cli%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fcli%2Freader%2Fcustom%2FConfigReader.java;h=95fc098c76b8c7ffa4696026704ec0ced02d59f4;hp=0000000000000000000000000000000000000000;hb=b3d2a00776a1a5e3a139d73ced859aa557c931af;hpb=d04e0863b86415749a8437241c57df0d32a3b133 diff --git a/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/reader/custom/ConfigReader.java b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/reader/custom/ConfigReader.java new file mode 100644 index 0000000000..95fc098c76 --- /dev/null +++ b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/reader/custom/ConfigReader.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.cli.reader.custom; + +import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import jline.console.completer.Completer; +import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry; +import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext; +import org.opendaylight.controller.netconf.cli.io.ConsoleContext; +import org.opendaylight.controller.netconf.cli.io.ConsoleIO; +import org.opendaylight.controller.netconf.cli.io.IOUtil; +import org.opendaylight.controller.netconf.cli.reader.AbstractReader; +import org.opendaylight.controller.netconf.cli.reader.ReadingException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Custom reader implementation for filter elements in get/get-config rpcs. This + * reader overrides the default anyxml reader and reads filter as a schema path. + */ +public class ConfigReader extends AbstractReader { + + public static final String SEPARATOR = "/"; + + private final CommandArgHandlerRegistry commandArgHandlerRegistry; + private final Map mappedModules; + private final Map mappedModulesNamespace; + + public ConfigReader(final ConsoleIO console, final SchemaContext remoteSchemaContext, + final CommandArgHandlerRegistry commandArgHandlerRegistry) { + super(console, remoteSchemaContext); + this.commandArgHandlerRegistry = commandArgHandlerRegistry; + + mappedModules = Maps.newHashMap(); + mappedModulesNamespace = Maps.newHashMap(); + for (final Module module : remoteSchemaContext.getModules()) { + final QName moduleQName = QName.create(module.getNamespace(), module.getRevision(), module.getName()); + mappedModules.put(moduleQName.getLocalName(), moduleQName); + mappedModulesNamespace.put(moduleQName.getNamespace(), moduleQName); + } + } + + // FIXME refactor + unite common code with FilterReader + + @Override + protected List> readWithContext(final DataSchemaNode schemaNode) throws IOException, ReadingException { + console.writeLn("Config " + schemaNode.getQName().getLocalName()); + console.writeLn("Submit path of the data to edit. Use TAB for autocomplete"); + + final String rawValue = console.read(); + + // FIXME isSkip check should be somewhere in abstractReader + if (isSkipInput(rawValue) || Strings.isNullOrEmpty(rawValue)) { + return Collections.emptyList(); + } + + final List filterPartsQNames = Lists.newArrayList(); + + for (final String part : rawValue.split(SEPARATOR)) { + final QName qName = IOUtil.qNameFromKeyString(part, mappedModules); + filterPartsQNames.add(qName); + } + + List> previous = readInnerNode(rawValue); + + for (final QName qName : Lists.reverse(filterPartsQNames).subList(1, filterPartsQNames.size())) { + previous = Collections.> singletonList(new CompositeNodeTOImpl(qName, null, + previous == null ? Collections.> emptyList() : previous)); + } + + final Node newNode = previous == null ? null + : new CompositeNodeTOImpl(schemaNode.getQName(), null, previous); + + return Collections.> singletonList(newNode); + } + + private List> readInnerNode(final String pathString) throws ReadingException { + final Optional schema = getCurrentNode(getSchemaContext(), pathString); + Preconditions.checkState(schema.isPresent(), "Unable to find schema for %s", pathString); + return commandArgHandlerRegistry.getGenericReader(getSchemaContext(), true).read(schema.get()); + } + + @Override + protected ConsoleContext getContext(final DataSchemaNode schemaNode) { + return new FilterConsoleContext(schemaNode, getSchemaContext()); + } + + private final class FilterConsoleContext extends BaseConsoleContext { + + private final SchemaContext remoteSchemaContext; + + public FilterConsoleContext(final DataSchemaNode schemaNode, final SchemaContext remoteSchemaContext) { + super(schemaNode); + this.remoteSchemaContext = remoteSchemaContext; + } + + @Override + protected List getAdditionalCompleters() { + return Collections. singletonList(new FilterCompleter(remoteSchemaContext)); + } + } + + private final class FilterCompleter implements Completer { + + private final SchemaContext remoteSchemaContext; + + public FilterCompleter(final SchemaContext remoteSchemaContext) { + this.remoteSchemaContext = remoteSchemaContext; + } + + @Override + public int complete(final String buffer, final int cursor, final List candidates) { + final int idx = buffer.lastIndexOf(SEPARATOR); + + final Optional currentNode = getCurrentNode(remoteSchemaContext, buffer); + if (currentNode.isPresent() && currentNode.get() instanceof DataNodeContainer) { + final Collection childNodes = ((DataNodeContainer) currentNode.get()).getChildNodes(); + final Collection transformed = Collections2.transform(childNodes, + new Function() { + @Override + public String apply(final DataSchemaNode input) { + return IOUtil.qNameToKeyString(input.getQName(), + mappedModulesNamespace.get(input.getQName().getNamespace()).getLocalName()); + } + }); + + fillCandidates(buffer.substring(idx + 1), candidates, transformed); + } + + return idx == -1 ? 0 : idx + 1; + } + + private void fillCandidates(final String buffer, final List candidates, + final Collection transformed) { + final SortedSet strings = new TreeSet<>(transformed); + + if (buffer == null) { + candidates.addAll(strings); + } else { + for (final String match : strings.tailSet(buffer)) { + if (!match.startsWith(buffer)) { + break; + } + candidates.add(match); + } + } + + if (candidates.size() == 1) { + candidates.set(0, candidates.get(0) + SEPARATOR); + } + } + + } + + private Optional getCurrentNode(DataSchemaNode parent, final String buffer) { + for (final String part : buffer.split(SEPARATOR)) { + if (IOUtil.isQName(part) == false) { + return Optional.of(parent); + } + + final QName qName; + try { + qName = IOUtil.qNameFromKeyString(part, mappedModules); + } catch (final ReadingException e) { + return Optional.of(parent); + } + if (parent instanceof DataNodeContainer) { + parent = ((DataNodeContainer) parent).getDataChildByName(qName); + } else { + // This should check if we are at the end of buffer ? + return Optional.of(parent); + } + } + return Optional.of(parent); + } + +}