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

org.dominokit.jackson.stream.impl.FastJsonWriter Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/*
 * Copyright © 2019 Dominokit
 *
 * 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
 *
 *     http://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.
 */
// @formatter:off
/*
 * Copyright 2014 Nicolas Morel
 *
 * 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
 *
 *     http://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.dominokit.jackson.stream.impl;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.dominokit.jackson.JacksonContextProvider;
import org.dominokit.jackson.exception.JsonSerializationException;
import org.dominokit.jackson.stream.JsonWriter;
import org.dominokit.jackson.stream.Stack;

/** FastJsonWriter class. */
public class FastJsonWriter implements JsonWriter {

  private static final Logger logger = Logger.getLogger("JsonWriter");

  /** The output data, containing at most one top-level array or object. */
  private final StringBuilder out;

  // Change by Ahmad Bawaneh, replace JSNI types with IsInterop types
  private Stack stack = JacksonContextProvider.get().integerStackFactory().make();

  {
    push(JsonScope.EMPTY_DOCUMENT);
  }

  /**
   * A string containing a full set of spaces for a single level of indentation, or null for no
   * pretty printing.
   */
  private String indent;

  /** The name/value separator; either ":" or ": ". */
  private String separator = ":";

  private boolean lenient;

  private String deferredUnescapeName;

  private String deferredName;

  private boolean serializeNulls = true;

  /**
   * Creates a new instance that writes a JSON-encoded stream to {@code out}.
   *
   * @param out a {@link java.lang.StringBuilder} object.
   */
  public FastJsonWriter(StringBuilder out) {
    if (out == null) {
      throw new NullPointerException("out == null");
    }
    this.out = out;
  }

  /** {@inheritDoc} */
  @Override
  public final void setIndent(String indent) {
    if (indent.length() == 0) {
      this.indent = null;
      this.separator = ":";
    } else {
      this.indent = indent;
      this.separator = ": ";
    }
  }

  /** {@inheritDoc} */
  @Override
  public final void setLenient(boolean lenient) {
    this.lenient = lenient;
  }

  /**
   * Returns true if this writer has relaxed syntax rules.
   *
   * @return a boolean.
   */
  public boolean isLenient() {
    return lenient;
  }

  /** {@inheritDoc} */
  @Override
  public final void setSerializeNulls(boolean serializeNulls) {
    this.serializeNulls = serializeNulls;
  }

  /**
   * {@inheritDoc}
   *
   * 

Returns true if object members are serialized when their value is null. This has no impact * on array elements. The default is true. */ @Override public final boolean getSerializeNulls() { return serializeNulls; } /** {@inheritDoc} */ @Override public FastJsonWriter beginArray() { writeDeferredName(); return open(JsonScope.EMPTY_ARRAY, "["); } /** {@inheritDoc} */ @Override public FastJsonWriter endArray() { return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]"); } /** {@inheritDoc} */ @Override public FastJsonWriter beginObject() { writeDeferredName(); return open(JsonScope.EMPTY_OBJECT, "{"); } /** {@inheritDoc} */ @Override public FastJsonWriter endObject() { return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}"); } /** Enters a new scope by appending any necessary whitespace and the given bracket. */ private FastJsonWriter open(int empty, String openBracket) { beforeValue(true); push(empty); out.append(openBracket); return this; } /** Closes the current scope by appending any necessary whitespace and the given bracket. */ private FastJsonWriter close(int empty, int nonempty, String closeBracket) { int context = peek(); if (context != nonempty && context != empty) { throw new IllegalStateException("Nesting problem."); } if (deferredUnescapeName != null || deferredName != null) { throw new IllegalStateException( "Dangling name: " + (deferredUnescapeName == null ? deferredName : deferredUnescapeName)); } stack.pop(); if (context == nonempty) { newline(); } out.append(closeBracket); return this; } private void push(int newTop) { stack.push(newTop); } /** Returns the value on the top of the stack. */ private int peek() { if (stack.size() == 0) { throw new IllegalStateException("JsonWriter is closed."); } return stack.peek(); } /** Replace the value on the top of the stack with the given value. */ private void replaceTop(int topOfStack) { stack.replaceTop(topOfStack); } /** {@inheritDoc} */ @Override public FastJsonWriter name(String name) { checkName(name); deferredName = name; return this; } /** {@inheritDoc} */ @Override public FastJsonWriter unescapeName(String name) { checkName(name); deferredUnescapeName = name; return this; } private void checkName(String name) { if (name == null) { throw new NullPointerException("name == null"); } if (deferredUnescapeName != null || deferredName != null) { throw new IllegalStateException(); } if (stack.size() == 0) { throw new IllegalStateException("JsonWriter is closed."); } } private void writeDeferredName() { if (deferredUnescapeName != null) { beforeName(); out.append('\"').append(deferredUnescapeName).append('\"'); deferredUnescapeName = null; } else if (deferredName != null) { beforeName(); string(deferredName); deferredName = null; } } /** {@inheritDoc} */ @Override public FastJsonWriter value(String value) { if (value == null) { return nullValue(); } writeDeferredName(); beforeValue(false); string(value); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter unescapeValue(String value) { if (value == null) { return nullValue(); } writeDeferredName(); beforeValue(false); out.append('\"').append(value).append('\"'); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter nullValue() { if (deferredUnescapeName != null || deferredName != null) { if (serializeNulls) { writeDeferredName(); } else { deferredUnescapeName = null; deferredName = null; return this; // skip the name and the value } } beforeValue(false); out.append("null"); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter cancelName() { if (deferredUnescapeName != null) { deferredUnescapeName = null; } else if (deferredName != null) { deferredName = null; } return this; } /** {@inheritDoc} */ @Override public FastJsonWriter value(boolean value) { writeDeferredName(); beforeValue(false); out.append(value ? "true" : "false"); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter value(double value) { if (Double.isNaN(value) || Double.isInfinite(value)) { throw new IllegalArgumentException("Numeric values must be finite, but was " + value); } writeDeferredName(); beforeValue(false); out.append(value); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter value(long value) { writeDeferredName(); beforeValue(false); out.append(value); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter value(Number value) { if (value == null) { return nullValue(); } writeDeferredName(); String string = value.toString(); if (!lenient && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { throw new IllegalArgumentException("Numeric values must be finite, but was " + value); } beforeValue(false); out.append(string); return this; } /** {@inheritDoc} */ @Override public FastJsonWriter rawValue(Object value) { if (value == null) { return nullValue(); } writeDeferredName(); beforeValue(false); out.append(value.toString()); return this; } /** {@inheritDoc} */ @Override public void flush() { if (stack.size() == 0) { throw new IllegalStateException("JsonWriter is closed."); } } /** {@inheritDoc} */ @Override public void close() { int size = stack.size(); if (size > 1 || size == 1 && stack.peek() != JsonScope.NONEMPTY_DOCUMENT) { logger.log(Level.SEVERE, "Incomplete document"); throw new JsonSerializationException("Incomplete document"); } stack.clear(); } private void string(String value) { out.append(JacksonContextProvider.get().stringifier().stringify(value)); } private void newline() { if (indent == null) { return; } out.append("\n"); for (int i = 1, size = stack.size(); i < size; i++) { out.append(indent); } } /** * Inserts any necessary separators and whitespace before a name. Also adjusts the stack to expect * the name's value. */ private void beforeName() { int context = peek(); if (context == JsonScope.NONEMPTY_OBJECT) { // first in object out.append(','); } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object! throw new IllegalStateException("Nesting problem."); } newline(); replaceTop(JsonScope.DANGLING_NAME); } /** * Inserts any necessary separators and whitespace before a literal value, inline array, or inline * object. Also adjusts the stack to expect either a closing bracket or another element. * * @param root true if the value is a new array or object, the two values permitted as top-level * elements. */ @SuppressWarnings("fallthrough") private void beforeValue(boolean root) { switch (peek()) { case JsonScope.NONEMPTY_DOCUMENT: if (!lenient) { throw new IllegalStateException("JSON must have only one top-level value."); } // fall-through case JsonScope.EMPTY_DOCUMENT: // first in document if (!lenient && !root) { throw new IllegalStateException("JSON must start with an array or an object."); } replaceTop(JsonScope.NONEMPTY_DOCUMENT); break; case JsonScope.EMPTY_ARRAY: // first in array replaceTop(JsonScope.NONEMPTY_ARRAY); newline(); break; case JsonScope.NONEMPTY_ARRAY: // another in array out.append(','); newline(); break; case JsonScope.DANGLING_NAME: // value for name out.append(separator); replaceTop(JsonScope.NONEMPTY_OBJECT); break; default: throw new IllegalStateException("Nesting problem."); } } /** {@inheritDoc} */ @Override public String getOutput() { return out.toString(); } } // @formatter:on





© 2015 - 2024 Weber Informatics LLC | Privacy Policy