All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.aot.nativex.BasicJsonWriter Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aot.nativex;

import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import org.springframework.aot.hint.TypeReference;

/**
 * Very basic json writer for the purposes of translating runtime hints to native
 * configuration.
 *
 * @author Stephane Nicoll
 */
class BasicJsonWriter {

	private final IndentingWriter writer;

	/**
	 * Create a new instance with the specified indent value.
	 * @param writer the writer to use
	 * @param singleIndent the value of one indent
	 */
	public BasicJsonWriter(Writer writer, String singleIndent) {
		this.writer = new IndentingWriter(writer, singleIndent);
	}

	/**
	 * Create a new instance using two whitespaces for the indent.
	 * @param writer the writer to use
	 */
	public BasicJsonWriter(Writer writer) {
		this(writer, "  ");
	}


	/**
	 * Write an object with the specified attributes. Each attribute is
	 * written according to its value type:
	 * 
    *
  • Map: write the value as a nested object
  • *
  • List: write the value as a nested array
  • *
  • Otherwise, write a single value
  • *
* @param attributes the attributes of the object */ public void writeObject(Map attributes) { writeObject(attributes, true); } /** * Write an array with the specified items. Each item in the * list is written either as a nested object or as an attribute * depending on its type. * @param items the items to write * @see #writeObject(Map) */ public void writeArray(List items) { writeArray(items, true); } private void writeObject(Map attributes, boolean newLine) { if (attributes.isEmpty()) { this.writer.print("{ }"); } else { this.writer.println("{").indented(writeAll(attributes.entrySet().iterator(), entry -> writeAttribute(entry.getKey(), entry.getValue()))).print("}"); } if (newLine) { this.writer.println(); } } private void writeArray(List items, boolean newLine) { if (items.isEmpty()) { this.writer.print("[ ]"); } else { this.writer.println("[") .indented(writeAll(items.iterator(), this::writeValue)).print("]"); } if (newLine) { this.writer.println(); } } private Runnable writeAll(Iterator it, Consumer writer) { return () -> { while (it.hasNext()) { writer.accept(it.next()); if (it.hasNext()) { this.writer.println(","); } else { this.writer.println(); } } }; } private void writeAttribute(String name, Object value) { this.writer.print(quote(name) + ": "); writeValue(value); } @SuppressWarnings("unchecked") private void writeValue(Object value) { if (value instanceof Map map) { writeObject((Map) map, false); } else if (value instanceof List list) { writeArray(list, false); } else if (value instanceof TypeReference typeReference) { this.writer.print(quote(typeReference.getName())); } else if (value instanceof CharSequence string) { this.writer.print(quote(escape(string))); } else if (value instanceof Boolean flag) { this.writer.print(Boolean.toString(flag)); } else { throw new IllegalStateException("unsupported type: " + value.getClass()); } } private String quote(String name) { return "\"" + name + "\""; } private static String escape(CharSequence input) { StringBuilder builder = new StringBuilder(); input.chars().forEach(c -> { switch (c) { case '"': builder.append("\\\""); break; case '\\': builder.append("\\\\"); break; case '/': builder.append("\\/"); break; case '\b': builder.append("\\b"); break; case '\f': builder.append("\\f"); break; case '\n': builder.append("\\n"); break; case '\r': builder.append("\\r"); break; case '\t': builder.append("\\t"); break; default: if (c <= 0x1F) { builder.append(String.format("\\u%04x", c)); } else { builder.append((char) c); } break; } }); return builder.toString(); } static class IndentingWriter extends Writer { private final Writer out; private final String singleIndent; private int level = 0; private String currentIndent = ""; private boolean prependIndent = false; IndentingWriter(Writer out, String singleIndent) { this.out = out; this.singleIndent = singleIndent; } /** * Write the specified text. * @param string the content to write */ public IndentingWriter print(String string) { write(string.toCharArray(), 0, string.length()); return this; } /** * Write the specified text and append a new line. * @param string the content to write */ public IndentingWriter println(String string) { write(string.toCharArray(), 0, string.length()); return println(); } /** * Write a new line. */ public IndentingWriter println() { String separator = System.lineSeparator(); try { this.out.write(separator.toCharArray(), 0, separator.length()); } catch (IOException ex) { throw new IllegalStateException(ex); } this.prependIndent = true; return this; } /** * Increase the indentation level and execute the {@link Runnable}. Decrease the * indentation level on completion. * @param runnable the code to execute within an extra indentation level */ public IndentingWriter indented(Runnable runnable) { indent(); runnable.run(); return outdent(); } /** * Increase the indentation level. */ private IndentingWriter indent() { this.level++; return refreshIndent(); } /** * Decrease the indentation level. */ private IndentingWriter outdent() { this.level--; return refreshIndent(); } private IndentingWriter refreshIndent() { this.currentIndent = this.singleIndent.repeat(Math.max(0, this.level)); return this; } @Override public void write(char[] chars, int offset, int length) { try { if (this.prependIndent) { this.out.write(this.currentIndent.toCharArray(), 0, this.currentIndent.length()); this.prependIndent = false; } this.out.write(chars, offset, length); } catch (IOException ex) { throw new IllegalStateException(ex); } } @Override public void flush() throws IOException { this.out.flush(); } @Override public void close() throws IOException { this.out.close(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy