d69171d2724ca7d70c9d4e1c86f945112877a062
[yangtools.git] / data / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / AbstractStringInstanceIdentifierCodec.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.yangtools.yang.data.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import javax.xml.XMLConstants;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.common.QNameModule;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
22 import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
25
26 /**
27  * Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a
28  * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name).
29  */
30 @Beta
31 public abstract class AbstractStringInstanceIdentifierCodec extends AbstractNamespaceCodec<YangInstanceIdentifier>
32         implements InstanceIdentifierCodec<String> {
33     @Override
34     protected final String serializeImpl(final YangInstanceIdentifier data) {
35         final StringBuilder sb = new StringBuilder();
36         DataSchemaContextNode<?> current = getDataContextTree().getRoot();
37         QNameModule lastModule = null;
38         for (var arg : data.getPathArguments()) {
39             current = current.getChild(arg);
40             checkArgument(current != null, "Invalid input %s: schema for argument %s (after %s) not found", data, arg,
41                     sb);
42
43             if (current.isMixin()) {
44                 /*
45                  * XML/YANG instance identifier does not have concept
46                  * of augmentation identifier, or list as whole which
47                  * identifies a mixin (same as the parent element),
48                  * so we can safely ignore it if it is part of path
49                  * (since child node) is identified in same fashion.
50                  */
51                 continue;
52             }
53
54             final var qname = arg.getNodeType();
55             sb.append('/');
56             appendQName(sb, qname, lastModule);
57             lastModule = qname.getModule();
58
59             if (arg instanceof NodeIdentifierWithPredicates) {
60                 for (var entry : ((NodeIdentifierWithPredicates) arg).entrySet()) {
61                     appendQName(sb.append('['), entry.getKey(), lastModule);
62                     sb.append("='").append(String.valueOf(entry.getValue())).append("']");
63                 }
64             } else if (arg instanceof NodeWithValue) {
65                 sb.append("[.='").append(((NodeWithValue<?>) arg).getValue()).append("']");
66             }
67         }
68         return sb.toString();
69     }
70
71     /**
72      * Returns DataSchemaContextTree associated with SchemaContext for which
73      * serialization / deserialization occurs.
74      *
75      * <p>
76      * Implementations MUST provide non-null Data Tree context, in order
77      * for correct serialization / deserialization of PathArguments,
78      * since XML representation does not have Augmentation arguments
79      * and does not provide path arguments for cases.
80      *
81      * <p>
82      * This effectively means same input XPath representation of Path Argument
83      * may result in different YangInstanceIdentifiers if models are different
84      * in uses of choices and cases.
85      *
86      * @return DataSchemaContextTree associated with SchemaContext for which
87      *         serialization / deserialization occurs.
88      */
89     protected abstract @NonNull DataSchemaContextTree getDataContextTree();
90
91     protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
92             final String value) {
93         return value;
94     }
95
96     @Override
97     protected final YangInstanceIdentifier deserializeImpl(final String data) {
98         return YangInstanceIdentifier.create(
99             new XpathStringParsingPathArgumentBuilder(this, requireNonNull(data)).build());
100     }
101
102     /**
103      * Create QName from unprefixed name, potentially taking last QNameModule encountered into account.
104      *
105      * @param lastModule last QNameModule encountered, potentially null
106      * @param localName Local name string
107      * @return A newly-created QName
108      */
109     protected QName createQName(final @Nullable QNameModule lastModule, final String localName) {
110         // This implementation handles both XML encoding, where we follow XML namespace rules and old JSON encoding,
111         // which is the same thing: always encode prefixes
112         return createQName(XMLConstants.DEFAULT_NS_PREFIX, localName);
113     }
114 }