4848
4949public class JsonOutput implements Closeable {
5050 private static final Logger LOG = Logger .getLogger (JsonOutput .class .getName ());
51- private static final int MAX_DEPTH = 10 ;
51+ static final int MAX_DEPTH = 10 ;
5252
5353 private static final Predicate <Class <?>> GSON_ELEMENT ;
5454
@@ -100,7 +100,7 @@ public class JsonOutput implements Closeable {
100100 ESCAPES = Collections .unmodifiableMap (builder );
101101 }
102102
103- private final Map <Predicate <Class <?>>, SafeBiConsumer < Object , Integer > > converters ;
103+ private final Map <Predicate <Class <?>>, DepthAwareConsumer > converters ;
104104 private final Appendable appendable ;
105105 private final Consumer <String > appender ;
106106 private Deque <Node > stack ;
@@ -126,32 +126,32 @@ public class JsonOutput implements Closeable {
126126
127127 // Order matters, since we want to handle null values first to avoid exceptions, and then then
128128 // common kinds of inputs next.
129- Map <Predicate <Class <?>>, SafeBiConsumer < Object , Integer > > builder = new LinkedHashMap <>();
130- builder .put (Objects ::isNull , (obj , depth ) -> append ("null" ));
131- builder .put (CharSequence .class ::isAssignableFrom , (obj , depth ) -> append (asString (obj )));
132- builder .put (Number .class ::isAssignableFrom , (obj , depth ) -> append (obj .toString ()));
129+ Map <Predicate <Class <?>>, DepthAwareConsumer > builder = new LinkedHashMap <>();
130+ builder .put (Objects ::isNull , (obj , maxDepth , depthRemaining ) -> append ("null" ));
131+ builder .put (CharSequence .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (asString (obj )));
132+ builder .put (Number .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (obj .toString ()));
133133 builder .put (
134- Boolean .class ::isAssignableFrom , (obj , depth ) -> append ((Boolean ) obj ? "true" : "false" ));
134+ Boolean .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append ((Boolean ) obj ? "true" : "false" ));
135135 builder .put (
136136 Date .class ::isAssignableFrom ,
137- (obj , depth ) -> append (String .valueOf (MILLISECONDS .toSeconds (((Date ) obj ).getTime ()))));
137+ (obj , maxDepth , depthRemaining ) -> append (String .valueOf (MILLISECONDS .toSeconds (((Date ) obj ).getTime ()))));
138138 builder .put (
139139 Instant .class ::isAssignableFrom ,
140- (obj , depth ) -> append (asString (DateTimeFormatter .ISO_INSTANT .format ((Instant ) obj ))));
141- builder .put (Enum .class ::isAssignableFrom , (obj , depth ) -> append (asString (obj )));
140+ (obj , maxDepth , depthRemaining ) -> append (asString (DateTimeFormatter .ISO_INSTANT .format ((Instant ) obj ))));
141+ builder .put (Enum .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (asString (obj )));
142142 builder .put (
143- File .class ::isAssignableFrom , (obj , depth ) -> append (((File ) obj ).getAbsolutePath ()));
144- builder .put (URI .class ::isAssignableFrom , (obj , depth ) -> append (asString ((obj ).toString ())));
143+ File .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (((File ) obj ).getAbsolutePath ()));
144+ builder .put (URI .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (asString ((obj ).toString ())));
145145 builder .put (
146146 URL .class ::isAssignableFrom ,
147- (obj , depth ) -> append (asString (((URL ) obj ).toExternalForm ())));
148- builder .put (UUID .class ::isAssignableFrom , (obj , depth ) -> append (asString (obj .toString ())));
147+ (obj , maxDepth , depthRemaining ) -> append (asString (((URL ) obj ).toExternalForm ())));
148+ builder .put (UUID .class ::isAssignableFrom , (obj , maxDepth , depthRemaining ) -> append (asString (obj .toString ())));
149149 builder .put (
150150 Level .class ::isAssignableFrom ,
151- (obj , depth ) -> append (asString (LogLevelMapping .getName ((Level ) obj ))));
151+ (obj , maxDepth , depthRemaining ) -> append (asString (LogLevelMapping .getName ((Level ) obj ))));
152152 builder .put (
153153 GSON_ELEMENT ,
154- (obj , depth ) -> {
154+ (obj , maxDepth , depthRemaining ) -> {
155155 LOG .log (
156156 Level .WARNING ,
157157 "Attempt to convert JsonElement from GSON. This functionality is deprecated. "
@@ -162,36 +162,36 @@ public class JsonOutput implements Closeable {
162162 // Special handling of asMap and toJson
163163 builder .put (
164164 cls -> getMethod (cls , "toJson" ) != null ,
165- (obj , depth ) -> convertUsingMethod ("toJson" , obj , depth ));
165+ (obj , maxDepth , depthRemaining ) -> convertUsingMethod ("toJson" , obj , maxDepth , depthRemaining ));
166166 builder .put (
167167 cls -> getMethod (cls , "asMap" ) != null ,
168- (obj , depth ) -> convertUsingMethod ("asMap" , obj , depth ));
168+ (obj , maxDepth , depthRemaining ) -> convertUsingMethod ("asMap" , obj , maxDepth , depthRemaining ));
169169 builder .put (
170170 cls -> getMethod (cls , "toMap" ) != null ,
171- (obj , depth ) -> convertUsingMethod ("toMap" , obj , depth ));
171+ (obj , maxDepth , depthRemaining ) -> convertUsingMethod ("toMap" , obj , maxDepth , depthRemaining ));
172172
173173 // And then the collection types
174174 builder .put (
175175 Collection .class ::isAssignableFrom ,
176- (obj , depth ) -> {
177- if (depth < 1 ) {
176+ (obj , maxDepth , depthRemaining ) -> {
177+ if (depthRemaining < 1 ) {
178178 throw new JsonException (
179- "Reached the maximum depth of " + MAX_DEPTH + " while writing JSON" );
179+ "Reached the maximum depth of " + maxDepth + " while writing JSON" );
180180 }
181181 beginArray ();
182182 ((Collection <?>) obj )
183183 .stream ()
184184 .filter (o -> (!(o instanceof Optional ) || ((Optional <?>) o ).isPresent ()))
185- .forEach (o -> write (o , depth - 1 ));
185+ .forEach (o -> write0 (o , maxDepth , depthRemaining - 1 ));
186186 endArray ();
187187 });
188188
189189 builder .put (
190190 Map .class ::isAssignableFrom ,
191- (obj , depth ) -> {
192- if (depth < 1 ) {
191+ (obj , maxDepth , depthRemaining ) -> {
192+ if (depthRemaining < 1 ) {
193193 throw new JsonException (
194- "Reached the maximum depth of " + MAX_DEPTH + " while writing JSON" );
194+ "Reached the maximum depth of " + maxDepth + " while writing JSON" );
195195 }
196196 beginObject ();
197197 ((Map <?, ?>) obj )
@@ -200,45 +200,45 @@ public class JsonOutput implements Closeable {
200200 if (value instanceof Optional && !((Optional ) value ).isPresent ()) {
201201 return ;
202202 }
203- name (String .valueOf (key )).write (value , depth - 1 );
203+ name (String .valueOf (key )).write0 (value , maxDepth , depthRemaining - 1 );
204204 });
205205 endObject ();
206206 });
207207 builder .put (
208208 Class ::isArray ,
209- (obj , depth ) -> {
210- if (depth < 1 ) {
209+ (obj , maxDepth , depthRemaining ) -> {
210+ if (depthRemaining < 1 ) {
211211 throw new JsonException (
212- "Reached the maximum depth of " + MAX_DEPTH + " while writing JSON" );
212+ "Reached the maximum depth of " + maxDepth + " while writing JSON" );
213213 }
214214 beginArray ();
215215 Stream .of ((Object []) obj )
216216 .filter (o -> (!(o instanceof Optional ) || ((Optional <?>) o ).isPresent ()))
217- .forEach (o -> write (o , depth - 1 ));
217+ .forEach (o -> write0 (o , maxDepth , depthRemaining - 1 ));
218218 endArray ();
219219 });
220220
221221 builder .put (
222222 Optional .class ::isAssignableFrom ,
223- (obj , depth ) -> {
223+ (obj , maxDepth , depthRemaining ) -> {
224224 Optional <?> optional = (Optional <?>) obj ;
225225 if (!optional .isPresent ()) {
226226 append ("null" );
227227 return ;
228228 }
229229
230- write (optional .get (), depth );
230+ write0 (optional .get (), maxDepth , depthRemaining );
231231 });
232232
233233 // Finally, attempt to convert as an object
234234 builder .put (
235235 cls -> true ,
236- (obj , depth ) -> {
237- if (depth < 1 ) {
236+ (obj , maxDepth , depthRemaining ) -> {
237+ if (depthRemaining < 1 ) {
238238 throw new JsonException (
239- "Reached the maximum depth of " + MAX_DEPTH + " while writing JSON" );
239+ "Reached the maximum depth of " + maxDepth + " while writing JSON" );
240240 }
241- mapObject (obj , depth - 1 );
241+ mapObject (obj , maxDepth , depthRemaining - 1 );
242242 });
243243
244244 this .converters = Collections .unmodifiableMap (builder );
@@ -313,13 +313,17 @@ public JsonOutput write(Object value) {
313313 return write (value , MAX_DEPTH );
314314 }
315315
316- public JsonOutput write (Object input , int depthRemaining ) {
316+ public JsonOutput write (Object value , int maxDepth ) {
317+ return write0 (value , maxDepth , maxDepth );
318+ }
319+
320+ private JsonOutput write0 (Object input , int maxDepth , int depthRemaining ) {
317321 converters .entrySet ().stream ()
318322 .filter (entry -> entry .getKey ().test (input == null ? null : input .getClass ()))
319323 .findFirst ()
320324 .map (Map .Entry ::getValue )
321325 .orElseThrow (() -> new JsonException ("Unable to write " + input ))
322- .consume (input , depthRemaining );
326+ .consume (input , maxDepth , depthRemaining );
323327
324328 return this ;
325329 }
@@ -381,7 +385,7 @@ private Method getMethod(Class<?> clazz, String methodName) {
381385 }
382386 }
383387
384- private JsonOutput convertUsingMethod (String methodName , Object toConvert , int depth ) {
388+ private JsonOutput convertUsingMethod (String methodName , Object toConvert , int maxDepth , int depthRemaining ) {
385389 try {
386390 Method method = getMethod (toConvert .getClass (), methodName );
387391 if (method == null ) {
@@ -390,13 +394,13 @@ private JsonOutput convertUsingMethod(String methodName, Object toConvert, int d
390394 }
391395 Object value = method .invoke (toConvert );
392396
393- return write (value , depth );
397+ return write0 (value , maxDepth , depthRemaining );
394398 } catch (ReflectiveOperationException e ) {
395399 throw new JsonException (e );
396400 }
397401 }
398402
399- private void mapObject (Object toConvert , int depthRemaining ) {
403+ private void mapObject (Object toConvert , int maxDepth , int depthRemaining ) {
400404 if (toConvert instanceof Class ) {
401405 write (((Class <?>) toConvert ).getName ());
402406 return ;
@@ -420,7 +424,7 @@ private void mapObject(Object toConvert, int depthRemaining) {
420424 Object value = pd .getReadMethod ().apply (toConvert );
421425 if (!Optional .empty ().equals (value )) {
422426 name (pd .getName ());
423- write (value , depthRemaining - 1 );
427+ write0 (value , maxDepth , depthRemaining - 1 );
424428 }
425429 }
426430 endObject ();
@@ -479,7 +483,7 @@ public void write(String text) {
479483 }
480484
481485 @ FunctionalInterface
482- private interface SafeBiConsumer < T , U > {
483- void consume (T t , U u );
486+ private interface DepthAwareConsumer {
487+ void consume (Object object , int maxDepth , int depthRemaining );
484488 }
485489}
0 commit comments