--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.restconf.openapi.impl;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.Deque;
+import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
+import org.opendaylight.restconf.openapi.model.OpenApiEntity;
+import org.opendaylight.restconf.openapi.model.SchemaEntity;
+
+public final class SchemaStream extends InputStream {
+ private final Deque<SchemaEntity> stack;
+ private final OpenApiBodyWriter writer;
+
+ private Reader reader;
+ private ReadableByteChannel channel;
+
+ public SchemaStream(final Deque<SchemaEntity> schemas, final OpenApiBodyWriter writer) {
+ this.stack = schemas;
+ this.writer = writer;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (reader == null) {
+ if (stack.isEmpty()) {
+ return -1;
+ }
+ reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(writeNextEntity(stack.pop())),
+ StandardCharsets.UTF_8));
+ }
+
+ var read = reader.read();
+ while (read == -1) {
+ if (stack.isEmpty()) {
+ return -1;
+ }
+ reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(writeNextEntity(stack.pop())),
+ StandardCharsets.UTF_8));
+ read = reader.read();
+ }
+
+ return read;
+ }
+
+ @Override
+ public int read(final byte[] array, final int off, final int len) throws IOException {
+ if (channel == null) {
+ if (stack.isEmpty()) {
+ return -1;
+ }
+ channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(stack.pop())));
+ }
+
+ var read = channel.read(ByteBuffer.wrap(array, off, len));
+ while (read == -1) {
+ if (stack.isEmpty()) {
+ return -1;
+ }
+ channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(stack.pop())));
+ read = channel.read(ByteBuffer.wrap(array, off, len));
+ }
+
+ return read;
+ }
+
+ private byte[] writeNextEntity(final OpenApiEntity entity) throws IOException {
+ writer.writeTo(entity, null, null, null, null, null, null);
+ return writer.readFrom();
+ }
+}
*/
package org.opendaylight.restconf.openapi.impl;
+import com.fasterxml.jackson.core.JsonGenerator;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
-import org.opendaylight.restconf.openapi.model.OpenApiEntity;
import org.opendaylight.restconf.openapi.model.SchemaEntity;
-import org.opendaylight.restconf.openapi.model.SchemasEntity;
import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
private final OpenApiBodyWriter writer;
private final EffectiveModelContext context;
private final boolean isForSingleModule;
+ private final ByteArrayOutputStream stream;
+ private final JsonGenerator generator;
private Reader reader;
private ReadableByteChannel channel;
+ private boolean eof;
public SchemasStream(final EffectiveModelContext context, final OpenApiBodyWriter writer,
- final Iterator<? extends Module> iterator, final boolean isForSingleModule) {
+ final Iterator<? extends Module> iterator, final boolean isForSingleModule,
+ final ByteArrayOutputStream stream, final JsonGenerator generator) {
this.iterator = iterator;
this.context = context;
this.writer = writer;
this.isForSingleModule = isForSingleModule;
+ this.stream = stream;
+ this.generator = generator;
}
@Override
public int read() throws IOException {
+ if (eof) {
+ return -1;
+ }
if (reader == null) {
+ generator.writeObjectFieldStart("schemas");
+ generator.flush();
reader = new BufferedReader(
- new InputStreamReader(new ByteArrayInputStream(writeNextEntity(
- new SchemasEntity(toComponents(iterator, context, isForSingleModule)))),
- StandardCharsets.UTF_8));
+ new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8));
+ stream.reset();
}
- return reader.read();
+ var read = reader.read();
+ while (read == -1) {
+ if (iterator.hasNext()) {
+ reader = new BufferedReader(new InputStreamReader(
+ new SchemaStream(toComponents(iterator.next(), context, isForSingleModule), writer),
+ StandardCharsets.UTF_8));
+ read = reader.read();
+ continue;
+ }
+ generator.writeEndObject();
+ generator.flush();
+ reader = new BufferedReader(
+ new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8));
+ stream.reset();
+ eof = true;
+ return reader.read();
+ }
+ return read;
}
@Override
public int read(final byte[] array, final int off, final int len) throws IOException {
+ if (eof) {
+ return -1;
+ }
if (channel == null) {
- channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(
- new SchemasEntity(toComponents(iterator, context, isForSingleModule)))));
+ generator.writeObjectFieldStart("schemas");
+ generator.flush();
+ channel = Channels.newChannel(new ByteArrayInputStream(stream.toByteArray()));
+ stream.reset();
}
- return channel.read(ByteBuffer.wrap(array, off, len));
- }
-
- private byte[] writeNextEntity(final OpenApiEntity next) throws IOException {
- writer.writeTo(next, null, null, null, null, null, null);
- return writer.readFrom();
+ var read = channel.read(ByteBuffer.wrap(array, off, len));
+ while (read == -1) {
+ if (iterator.hasNext()) {
+ channel = Channels.newChannel(
+ new SchemaStream(toComponents(iterator.next(), context, isForSingleModule), writer));
+ read = channel.read(ByteBuffer.wrap(array, off, len));
+ continue;
+ }
+ generator.writeEndObject();
+ generator.flush();
+ channel = Channels.newChannel(new ByteArrayInputStream(stream.toByteArray()));
+ stream.reset();
+ eof = true;
+ return channel.read(ByteBuffer.wrap(array, off, len));
+ }
+ return read;
}
- private static Deque<SchemaEntity> toComponents(final Iterator<? extends Module> iterator,
- final EffectiveModelContext context, final boolean isForSingleModule) {
+ private static Deque<SchemaEntity> toComponents(final Module module, final EffectiveModelContext context,
+ final boolean isForSingleModule) {
final var result = new ArrayDeque<SchemaEntity>();
- while (iterator.hasNext()) {
- final var module = iterator.next();
- final var definitionNames = new DefinitionNames();
- final var stack = SchemaInferenceStack.of(context);
- final var moduleName = module.getName();
- if (isForSingleModule) {
- definitionNames.addUnlinkedName(moduleName + "_module");
- }
- final var children = new ArrayList<DataSchemaNode>();
- for (final var rpc : module.getRpcs()) {
- stack.enterSchemaTree(rpc.getQName());
- final var rpcName = rpc.getQName().getLocalName();
- final var rpcInput = rpc.getInput();
- if (!rpcInput.getChildNodes().isEmpty()) {
- final var input = new SchemaEntity(rpcInput, moduleName + "_" + rpcName + INPUT_SUFFIX, null,
- OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
- result.add(input);
- stack.enterSchemaTree(rpcInput.getQName());
- for (final var child : rpcInput.getChildNodes()) {
- if (!children.contains(child)) {
- children.add(child);
- processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
- false);
- }
+ final var definitionNames = new DefinitionNames();
+ final var stack = SchemaInferenceStack.of(context);
+ final var moduleName = module.getName();
+ if (isForSingleModule) {
+ definitionNames.addUnlinkedName(moduleName + "_module");
+ }
+ final var children = new ArrayList<DataSchemaNode>();
+ for (final var rpc : module.getRpcs()) {
+ stack.enterSchemaTree(rpc.getQName());
+ final var rpcName = rpc.getQName().getLocalName();
+ final var rpcInput = rpc.getInput();
+ if (!rpcInput.getChildNodes().isEmpty()) {
+ final var input = new SchemaEntity(rpcInput, moduleName + "_" + rpcName + INPUT_SUFFIX, null,
+ OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
+ result.add(input);
+ stack.enterSchemaTree(rpcInput.getQName());
+ for (final var child : rpcInput.getChildNodes()) {
+ if (!children.contains(child)) {
+ children.add(child);
+ processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
+ false);
}
- stack.exit();
}
- final var rpcOutput = rpc.getOutput();
- if (!rpcOutput.getChildNodes().isEmpty()) {
- final var output = new SchemaEntity(rpcOutput, moduleName + "_" + rpcName + OUTPUT_SUFFIX, null,
- OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
- result.add(output);
- stack.enterSchemaTree(rpcOutput.getQName());
- for (final var child : rpcOutput.getChildNodes()) {
- if (!children.contains(child)) {
- children.add(child);
- processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
- false);
- }
+ stack.exit();
+ }
+ final var rpcOutput = rpc.getOutput();
+ if (!rpcOutput.getChildNodes().isEmpty()) {
+ final var output = new SchemaEntity(rpcOutput, moduleName + "_" + rpcName + OUTPUT_SUFFIX, null,
+ OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
+ result.add(output);
+ stack.enterSchemaTree(rpcOutput.getQName());
+ for (final var child : rpcOutput.getChildNodes()) {
+ if (!children.contains(child)) {
+ children.add(child);
+ processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
+ false);
}
- stack.exit();
}
stack.exit();
}
+ stack.exit();
+ }
- for (final var childNode : module.getChildNodes()) {
- processDataAndActionNodes(childNode, moduleName, stack, definitionNames, result, moduleName,
- true);
- }
+ for (final var childNode : module.getChildNodes()) {
+ processDataAndActionNodes(childNode, moduleName, stack, definitionNames, result, moduleName,
+ true);
}
return result;
}