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