blob: 820f9f9c9a87c4d4d5afec5739cb9d452647afc0 [file] [log] [blame]
/*
* 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;
}
}
}