| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.johnzon.mapper; |
| |
| import javax.json.JsonValue; |
| import javax.json.stream.JsonGenerator; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| |
| public class DynamicMappingGenerator implements MappingGenerator { |
| private final MappingGenerator delegate; |
| private final Runnable writeStart; |
| private final Runnable writeEnd; |
| private final String keyName; |
| |
| protected InObjectOrPrimitiveJsonGenerator generator; |
| |
| public DynamicMappingGenerator(final MappingGenerator delegate, |
| final Runnable writeStart, |
| final Runnable writeEnd, |
| final String keyName) { |
| this.delegate = delegate; |
| this.writeStart = writeStart; |
| this.writeEnd = writeEnd; |
| this.keyName = keyName; |
| } |
| |
| protected JsonGenerator getRawJsonGenerator() { |
| return delegate.getJsonGenerator(); |
| } |
| |
| @Override |
| public JsonGenerator getJsonGenerator() { |
| return generator == null ? generator = new InObjectOrPrimitiveJsonGenerator( |
| getRawJsonGenerator(), writeStart, keyName, writeEnd) : generator; |
| } |
| |
| @Override |
| public MappingGenerator writeObject(final String key, final Object o, final JsonGenerator generator) { |
| return delegate.writeObject(key, o, ensureGenerator(generator)); |
| } |
| |
| @Override |
| public MappingGenerator writeObject(final Object o, final JsonGenerator generator) { |
| return delegate.writeObject(o, ensureGenerator(generator)); |
| } |
| |
| private JsonGenerator ensureGenerator(final JsonGenerator generator) { |
| if (this.generator != null && this.generator != generator && this.generator.delegate != generator) { |
| this.generator = null; |
| reset(); |
| } |
| return getJsonGenerator(); // ensure we wrap it |
| } |
| |
| protected void reset() { |
| // no-op |
| } |
| |
| public void flushIfNeeded() { |
| if (generator != null) { |
| generator.endIfNeeded(); |
| } |
| } |
| |
| private enum WritingState { |
| NONE, |
| WROTE_START, |
| DONT_WRITE_END |
| } |
| |
| public static class InObjectOrPrimitiveJsonGenerator implements JsonGenerator { |
| private final JsonGenerator delegate; |
| private final Runnable writeStart; |
| private final Runnable writeEnd; |
| private final String keyIfNoObject; |
| private WritingState state = WritingState.NONE; // todo: we need a stack (linkedlist) here to be accurate |
| private int nested = 0; |
| |
| private InObjectOrPrimitiveJsonGenerator(final JsonGenerator generator, final Runnable writeStart, |
| final String keyName, final Runnable writeEnd) { |
| this.delegate = generator; |
| this.writeStart = writeStart; |
| this.writeEnd = writeEnd; |
| this.keyIfNoObject = keyName; |
| } |
| |
| private void ensureStart() { |
| if (state != WritingState.NONE) { |
| return; |
| } |
| writeStart.run(); |
| state = WritingState.WROTE_START; |
| } |
| |
| @Override |
| public JsonGenerator writeStartObject() { |
| if (state == WritingState.NONE) { |
| ensureStart(); |
| } else { |
| nested++; |
| delegate.writeStartObject(); |
| } |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartObject(final String name) { |
| if (state == WritingState.NONE) { |
| ensureStart(); |
| } |
| nested++; |
| delegate.writeStartObject(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartArray() { |
| if (state != WritingState.NONE) { |
| nested++; |
| } |
| if (keyIfNoObject != null && state == WritingState.NONE) { |
| state = WritingState.DONT_WRITE_END; // skip writeEnd since the impl will do it |
| return delegate.writeStartArray(keyIfNoObject); |
| } else if (state == WritingState.NONE) { |
| ensureStart(); |
| return this; |
| } |
| delegate.writeStartArray(); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartArray(final String name) { |
| if (state != WritingState.NONE) { |
| nested++; |
| } |
| ensureStart(); |
| delegate.writeStartArray(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeKey(final String name) { |
| ensureStart(); |
| delegate.writeKey(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final JsonValue value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final String value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final BigInteger value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final BigDecimal value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final int value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final long value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final double value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final boolean value) { |
| ensureStart(); |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeNull(final String name) { |
| ensureStart(); |
| delegate.writeNull(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final JsonValue value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final BigDecimal value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final BigInteger value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final int value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final long value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final double value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(boolean value) { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.write(keyIfNoObject, value); |
| return this; |
| } |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeNull() { |
| if (isWritingPrimitive()) { |
| state = WritingState.DONT_WRITE_END; |
| delegate.writeNull(keyIfNoObject); |
| return this; |
| } |
| delegate.writeNull(); |
| return this; |
| } |
| |
| @Override |
| public void close() { |
| delegate.close(); |
| } |
| |
| @Override |
| public void flush() { |
| delegate.flush(); |
| } |
| |
| @Override |
| public JsonGenerator writeEnd() { |
| return doWriteEnd(false); |
| } |
| |
| private JsonGenerator doWriteEnd(final boolean useDelegate) { |
| if (nested == 0 && state == WritingState.WROTE_START) { |
| state = WritingState.NONE; |
| } |
| if (nested > 0) { |
| nested--; |
| } |
| if (!useDelegate && nested == 0 && SkipEnclosingWriteEnd.NOOP != writeEnd) { |
| writeEnd.run(); |
| } else { |
| if (nested == 0) { |
| final JsonGenerator unwrap = unwrap(delegate); |
| unwrap.writeEnd(); |
| } else { |
| delegate.writeEnd(); |
| } |
| } |
| return this; |
| } |
| |
| private JsonGenerator unwrap(final JsonGenerator delegate) { |
| JsonGenerator current = delegate; |
| while (SkipLastWriteEndGenerator.class.isInstance(current)) { |
| current = SkipLastWriteEndGenerator.class.cast(current).delegate; |
| } |
| return current; |
| } |
| |
| public void endIfNeeded() { |
| endIfNeeded(this); |
| } |
| |
| private boolean isWritingPrimitive() { |
| return state == WritingState.NONE && keyIfNoObject != null; |
| } |
| |
| public static void endIfNeeded(final JsonGenerator generator) { |
| if (!InObjectOrPrimitiveJsonGenerator.class.isInstance(generator)) { |
| return; |
| } |
| final InObjectOrPrimitiveJsonGenerator jsonGenerator = InObjectOrPrimitiveJsonGenerator.class.cast(generator); |
| if (jsonGenerator.state == WritingState.WROTE_START) { |
| jsonGenerator.doWriteEnd(true); |
| jsonGenerator.state = WritingState.DONT_WRITE_END; |
| } |
| } |
| } |
| |
| private static abstract class DelegatingGenerator implements JsonGenerator { |
| protected final JsonGenerator delegate; |
| |
| protected DelegatingGenerator(final JsonGenerator generator) { |
| this.delegate = generator; |
| } |
| |
| @Override |
| public JsonGenerator writeKey(final String name) { |
| delegate.writeKey(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final JsonValue value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final String value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final BigInteger value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final BigDecimal value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final int value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final long value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final double value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String name, final boolean value) { |
| delegate.write(name, value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeNull(final String name) { |
| delegate.writeNull(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final JsonValue value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final String value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final BigDecimal value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final BigInteger value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final int value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final long value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(final double value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator write(boolean value) { |
| delegate.write(value); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeNull() { |
| delegate.writeNull(); |
| return this; |
| } |
| |
| @Override |
| public void close() { |
| delegate.close(); |
| } |
| |
| @Override |
| public void flush() { |
| delegate.flush(); |
| } |
| } |
| |
| private static class SkipLastWriteEndGenerator extends DelegatingGenerator { |
| private int level = -1; |
| |
| private SkipLastWriteEndGenerator(final JsonGenerator generator) { |
| super(generator); |
| } |
| |
| @Override |
| public JsonGenerator writeStartObject() { |
| level++; |
| if (level > 0) { |
| delegate.writeStartObject(); |
| } |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartObject(final String name) { |
| level++; |
| if (level == 0) { |
| level++; // force a writeEnd since it will be a nested object and not the object we are writing |
| } |
| delegate.writeStartObject(name); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartArray() { |
| level++; |
| delegate.writeStartArray(); |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeStartArray(final String name) { |
| delegate.writeStartArray(name); |
| level++; |
| return this; |
| } |
| |
| @Override |
| public JsonGenerator writeEnd() { |
| if (level > 0) { |
| delegate.writeEnd(); |
| } |
| level--; |
| return this; |
| } |
| } |
| |
| public static class SkipEnclosingWriteEnd extends DynamicMappingGenerator { |
| private static final Runnable NOOP = () -> { |
| }; |
| private final JsonGenerator rawGenerator; |
| |
| private SkipLastWriteEndGenerator skippingGenerator; |
| |
| public SkipEnclosingWriteEnd(final MappingGenerator delegate, final String keyName, final JsonGenerator generator) { |
| super(delegate, NOOP, NOOP, keyName); |
| this.rawGenerator = generator; |
| } |
| |
| @Override |
| protected JsonGenerator getRawJsonGenerator() { |
| return rawGenerator; |
| } |
| |
| @Override |
| public JsonGenerator getJsonGenerator() { |
| if (skippingGenerator == null) { |
| skippingGenerator = new SkipLastWriteEndGenerator(super.getJsonGenerator()); |
| } |
| return skippingGenerator; |
| } |
| |
| @Override |
| protected void reset() { |
| super.reset(); |
| skippingGenerator = null; |
| } |
| } |
| } |