Eliminate restconf.nb.rfc8040.jersey
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / server / spi / NormalizedFormattableBody.java
1 /*
2  * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.restconf.server.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects.ToStringHelper;
13 import com.google.common.base.VerifyException;
14 import com.google.gson.stream.JsonWriter;
15 import java.io.IOException;
16 import java.io.OutputStream;
17 import javax.xml.stream.XMLStreamException;
18 import javax.xml.stream.XMLStreamWriter;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.opendaylight.restconf.api.FormattableBody;
21 import org.opendaylight.restconf.api.query.PrettyPrintParam;
22 import org.opendaylight.restconf.server.api.DatabindContext;
23 import org.opendaylight.restconf.server.api.DatabindFormattableBody;
24 import org.opendaylight.restconf.server.api.DatabindPath.Data;
25 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
31 import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
32 import org.opendaylight.yangtools.yang.data.codec.xml.XmlCodecFactory;
33 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
34
35 /**
36  * A {@link FormattableBody} representing a data resource.
37  */
38 @NonNullByDefault
39 public abstract sealed class NormalizedFormattableBody<N extends NormalizedNode> extends DatabindFormattableBody
40         permits DataFormattableBody, RootFormattableBody {
41     private final NormalizedNodeWriterFactory writerFactory;
42     private final N data;
43
44     NormalizedFormattableBody(final DatabindContext databind, final NormalizedNodeWriterFactory writerFactory,
45             final N data) {
46         super(databind);
47         this.writerFactory = requireNonNull(writerFactory);
48         this.data = requireNonNull(data);
49     }
50
51     public static NormalizedFormattableBody<?> of(final Data path, final NormalizedNode data,
52             final NormalizedNodeWriterFactory writerFactory) {
53         final var inference = path.inference();
54         if (inference.isEmpty()) {
55             // Read of the entire /data resource
56             if (data instanceof ContainerNode container) {
57                 return new RootFormattableBody(path.databind(), writerFactory, container);
58             }
59             throw new VerifyException("Unexpected root data contract " + data.contract());
60         }
61
62         // Read of a sub-resource. We need to adjust the inference to point to the NormalizedNode parent of the node
63         // being output.
64         final Inference parentInference;
65         if (data instanceof MapEntryNode || data instanceof LeafSetEntryNode || data instanceof UnkeyedListEntryNode) {
66             parentInference = inference;
67         } else {
68             final var stack = inference.toSchemaInferenceStack();
69             stack.exitToDataTree();
70             parentInference = stack.toInference();
71         }
72
73         return new DataFormattableBody<>(path.databind(), parentInference, data, writerFactory);
74     }
75
76     /**
77      * Return data.
78      *
79      * @return data
80      */
81     public final N data() {
82         return data;
83     }
84
85     @Override
86     protected final void formatToJSON(final DatabindContext databind, final PrettyPrintParam prettyPrint,
87             final OutputStream out) throws IOException {
88         try (var writer = FormattableBodySupport.createJsonWriter(out, prettyPrint)) {
89             formatToJSON(databind.jsonCodecs(), data, writer);
90         }
91     }
92
93     protected abstract void formatToJSON(JSONCodecFactory codecs, N data, JsonWriter writer) throws IOException;
94
95     @Override
96     protected final void formatToXML(final DatabindContext databind, final PrettyPrintParam prettyPrint,
97             final OutputStream out) throws IOException {
98         final var writer = FormattableBodySupport.createXmlWriter(out, prettyPrint);
99         try {
100             formatToXML(databind.xmlCodecs(), data, writer);
101             writer.close();
102         } catch (XMLStreamException e) {
103             throw new IOException("Failed to write data", e);
104         }
105     }
106
107     protected abstract void formatToXML(XmlCodecFactory codecs, N data, XMLStreamWriter writer)
108         throws IOException, XMLStreamException;
109
110     protected final NormalizedNodeWriter newWriter(final NormalizedNodeStreamWriter streamWriter) {
111         return writerFactory.newWriter(streamWriter);
112     }
113
114     final void writeTo(final NormalizedNode toWrite, final NormalizedNodeStreamWriter streamWriter)
115             throws IOException {
116         try (var writer = newWriter(streamWriter)) {
117             writer.write(toWrite);
118         }
119     }
120
121     @Override
122     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
123         return helper.add("body", data.prettyTree());
124     }
125
126
127 }