-/*
- * 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.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
-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<DataSchemaNode> {
-
- public static final String SEPARATOR = "/";
-
- private final CommandArgHandlerRegistry commandArgHandlerRegistry;
- private final Map<String, QName> mappedModules;
- private final Map<URI, QName> 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<NormalizedNode<?, ?>> 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<QName> filterPartsQNames = Lists.newArrayList();
-
- for (final String part : rawValue.split(SEPARATOR)) {
- final QName qName = IOUtil.qNameFromKeyString(part, mappedModules);
- filterPartsQNames.add(qName);
- }
-
- List<? extends NormalizedNode<?, ?>> previous = readInnerNode(rawValue);
-
- for (final QName qName : Lists.reverse(filterPartsQNames).subList(1, filterPartsQNames.size())) {
- previous = Collections.<NormalizedNode<?, ?>>singletonList(
- ImmutableContainerNodeBuilder.create()
- .withNodeIdentifier(new NodeIdentifier(qName))
- .withValue(previous == null ? Collections.<DataContainerChild<?, ?>>emptyList() : (Collection) previous).build()
- );
- }
-
- if (previous == null) {
- return Collections.singletonList(null);
- }
-
- final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = ImmutableContainerNodeBuilder.create();
- builder.withNodeIdentifier(new NodeIdentifier(schemaNode.getQName()));
- builder.withValue((Collection<DataContainerChild<?, ?>>) previous);
-
- return Collections.<NormalizedNode<?, ?>> singletonList(builder.build());
- }
-
- private List<NormalizedNode<?, ?>> readInnerNode(final String pathString) throws ReadingException {
- final Optional<DataSchemaNode> 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<DataSchemaNode> {
-
- private final SchemaContext remoteSchemaContext;
-
- public FilterConsoleContext(final DataSchemaNode schemaNode, final SchemaContext remoteSchemaContext) {
- super(schemaNode);
- this.remoteSchemaContext = remoteSchemaContext;
- }
-
- @Override
- protected List<Completer> getAdditionalCompleters() {
- return Collections.<Completer> 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<CharSequence> candidates) {
- final int idx = buffer.lastIndexOf(SEPARATOR);
-
- final Optional<DataSchemaNode> currentNode = getCurrentNode(remoteSchemaContext, buffer);
- if (currentNode.isPresent() && currentNode.get() instanceof DataNodeContainer) {
- final Collection<DataSchemaNode> childNodes = ((DataNodeContainer) currentNode.get()).getChildNodes();
- final Collection<String> transformed = Collections2.transform(childNodes,
- new Function<DataSchemaNode, String>() {
- @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<CharSequence> candidates,
- final Collection<String> transformed) {
- final SortedSet<String> 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<DataSchemaNode> 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);
- }
-
-}