001/* 002 * ============================================================================ 003 * Copyright © 2002-2026 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.jsonbuilder.internal; 019 020import org.apiguardian.api.API; 021import org.tquadrat.foundation.annotation.ClassVersion; 022import org.tquadrat.foundation.jsonbuilder.JSONArray; 023import org.tquadrat.foundation.jsonbuilder.JSONBuilder; 024import org.tquadrat.foundation.jsonbuilder.JSONObject; 025import org.tquadrat.foundation.jsonbuilder.JSONValue; 026import org.tquadrat.foundation.lang.value.Dimension; 027import org.tquadrat.foundation.lang.value.DimensionedValue; 028 029import java.io.IOException; 030import java.io.UncheckedIOException; 031import java.math.BigDecimal; 032import java.math.BigInteger; 033import java.util.Formatter; 034import java.util.Iterator; 035import java.util.LinkedHashMap; 036import java.util.Optional; 037import java.util.SequencedCollection; 038import java.util.SequencedMap; 039import java.util.StringJoiner; 040 041import static java.lang.Integer.max; 042import static java.util.Collections.unmodifiableSequencedCollection; 043import static org.apiguardian.api.API.Status.INTERNAL; 044import static org.tquadrat.foundation.jsonbuilder.JSONLiteral.NULL; 045import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_STRING; 046import static org.tquadrat.foundation.lang.Objects.hash; 047import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 048import static org.tquadrat.foundation.lang.Objects.requireNotBlankArgument; 049 050/** 051 * <p>{@summary The implementation for the interface 052 * {@link JSONObject}.}</p> 053 * 054 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 055 * @version $Id: JSONObjectImpl.java 1195 2026-04-15 21:33:40Z tquadrat $ 056 * @since 0.25.0 057 * 058 * @UMLGraph.link 059 */ 060@ClassVersion( sourceVersion = "$Id: JSONObjectImpl.java 1195 2026-04-15 21:33:40Z tquadrat $" ) 061@API( status = INTERNAL, since = "0.25.0" ) 062public final class JSONObjectImpl implements JSONObject 063{ 064 /*------------*\ 065 ====** Attributes **======================================================= 066 \*------------*/ 067 /** 068 * The reference to the 069 * {@link JSONBuilder} 070 * that was used to create this {@code JSONObject}, and that is used to 071 * create the members. 072 */ 073 private final JSONBuilderImpl m_Builder; 074 075 /** 076 * The members of this {@code JSONObject}. 077 */ 078 private final SequencedMap<String,JSONValue> m_Members = new LinkedHashMap<>(); 079 080 /*--------------*\ 081 ====** Constructors **===================================================== 082 \*--------------*/ 083 /** 084 * Creates a new instance of {@code JSONObjectImpl}. 085 * 086 * @param builder The reference to the 087 * {@link JSONBuilder}. 088 */ 089 public JSONObjectImpl( final JSONBuilderImpl builder ) 090 { 091 m_Builder = requireNonNullArgument( builder, "builder" ); 092 } // JSONObjectImpl() 093 094 /** 095 * <p>{summary Creates a new instance of {@code JSONObjectImpl} from the 096 * given other object.}</p> 097 * <p>The new object is a deep copy of the given instance.</p> 098 * 099 * @param other The other JSON object. 100 */ 101 public JSONObjectImpl( final JSONObjectImpl other ) 102 { 103 m_Builder = requireNonNullArgument( other, "other" ).m_Builder; 104 for( final var entry : other.m_Members.entrySet() ) 105 { 106 final var name = entry.getKey(); 107 final var value = entry.getValue(); 108 m_Members.put( name, switch( value ) 109 { 110 case null -> NULL; 111 case JSONArrayImpl array -> new JSONArrayImpl( array ); 112 case JSONObjectImpl object -> new JSONObjectImpl( object ); 113 default -> value; 114 }); 115 } 116 } // JSONObjectImpl() 117 118 /*---------*\ 119 ====** Methods **========================================================== 120 \*---------*/ 121 /** 122 * {@inheritDoc} 123 */ 124 @Override 125 public final boolean contains( final String name ) { return m_Members.containsKey( name ); } 126 127 /** 128 * {@inheritDoc} 129 */ 130 @Override 131 public final boolean equals( final Object o ) 132 { 133 var retValue = this == o; 134 if( !retValue && o instanceof final JSONObjectImpl other ) 135 { 136 retValue = m_Builder.equals( other.m_Builder ) 137 && m_Members.equals( other.m_Members ); 138 } 139 140 //---* Done *---------------------------------------------------------- 141 return retValue; 142 } // equals() 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override 148 public void formatTo( final Formatter formatter, final int flags, final int width, final int precision ) 149 { 150 final var indentation1 = "\n" + (width <= 0 ? EMPTY_STRING : " ".repeat( width )); 151 final var newWidth = max( 0, width ) + m_Builder.getIndentation(); 152 final var indentation2 = "\n" + " ".repeat( newWidth ); 153 final var appendable = formatter.out(); 154 try 155 { 156 switch( m_Members.size() ) 157 { 158 case 0 -> appendable.append( "{}" ); 159 case 1 -> 160 { 161 final var entry = m_Members.firstEntry(); 162 appendable.append( "{ " ) 163 .append( '"' ) 164 .append( entry.getKey() ) 165 .append( '"' ) 166 .append( " : " ); 167 entry.getValue().formatTo( formatter, flags, newWidth, precision ); 168 appendable.append( " }" ); 169 } 170 default -> 171 { 172 var isFirst = true; 173 appendable.append( "{" ); 174 for( final var entry : m_Members.entrySet() ) 175 { 176 if( isFirst ) 177 { 178 isFirst = false; 179 } 180 else 181 { 182 appendable.append( ',' ); 183 } 184 appendable.append( indentation2 ) 185 .append( '"' ) 186 .append( entry.getKey() ) 187 .append( '"' ) 188 .append( " : " ); 189 entry.getValue().formatTo( formatter, flags, newWidth, precision ); 190 } 191 appendable.append( indentation1 ) 192 .append( "}" ); 193 } 194 } 195 } 196 catch( final IOException e ) 197 { 198 throw new UncheckedIOException( e.getMessage(), e ); 199 } 200 } // formatTo() 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override 206 public final Optional<JSONValue> get( final String name ) 207 { 208 final var retValue = Optional.ofNullable( m_Members.get( name ) ); 209 210 //---* Done *---------------------------------------------------------- 211 return retValue; 212 } // get() 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public final int hashCode() { return hash( m_Builder, m_Members ); } 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override 224 public final boolean isEmpty() { return m_Members.isEmpty(); } 225 226 /** 227 * {@inheritDoc} 228 */ 229 @Override 230 public final Iterator<JSONValue> iterator() { return m_Members.sequencedValues().iterator(); } 231 232 /** 233 * {@inheritDoc} 234 */ 235 @Override 236 public JSONObject merge( final JSONObject object ) 237 { 238 m_Members.putAll( ((JSONObjectImpl) requireNonNullArgument( object, "object" )).m_Members ); 239 240 //---* Done *---------------------------------------------------------- 241 return this; 242 } // merge() 243 244 /** 245 * {@inheritDoc} 246 */ 247 @Override 248 public final SequencedCollection<String> names() 249 { 250 final var retValue = unmodifiableSequencedCollection( m_Members.sequencedKeySet() ); 251 252 //---* Done *---------------------------------------------------------- 253 return retValue; 254 } // names() 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override 260 public final JSONObject remove( final String name ) 261 { 262 m_Members.remove( name ); 263 264 //---* Done *---------------------------------------------------------- 265 return this; 266 } // remove() 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public final JSONObject set( final String name, final BigDecimal value ) 273 { 274 return set( name, m_Builder.valueOf( value ) ); 275 } // set 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public final JSONObject set( final String name, final BigInteger value ) 282 { 283 return set( name, m_Builder.valueOf( value ) ); 284 } // set 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 public <T extends Dimension> JSONObject set( final String name, final DimensionedValue<T> value, final T targetUnit ) 291 { 292 return set( name, m_Builder.valueOf( value, targetUnit ) ); 293 } // set() 294 295 /** 296 * {@inheritDoc} 297 */ 298 @Override 299 public final JSONObject set( final String name, final double value ) 300 { 301 return set( name, m_Builder.valueOf( value ) ); 302 } // set 303 304 /** 305 * {@inheritDoc} 306 */ 307 @Override 308 public final JSONObject set( final String name, final Double value ) 309 { 310 return set( name, m_Builder.valueOf( value ) ); 311 } // set 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override 317 public final JSONObject set( final String name, final float value ) 318 { 319 return set( name, m_Builder.valueOf( value ) ); 320 } // set 321 322 /** 323 * {@inheritDoc} 324 */ 325 @Override 326 public final JSONObject set( final String name, final Float value ) 327 { 328 return set( name, m_Builder.valueOf( value ) ); 329 } // set 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override 335 public final JSONObject set( final String name, final int value ) 336 { 337 return set( name, m_Builder.valueOf( value ) ); 338 } // set 339 340 /** 341 * {@inheritDoc} 342 */ 343 @Override 344 public final JSONObject set( final String name, final Integer value ) 345 { 346 return set( name, m_Builder.valueOf( value ) ); 347 } // set 348 349 /** 350 * {@inheritDoc} 351 */ 352 @Override 353 public final JSONObject set( final String name, final JSONValue value ) 354 { 355 m_Members.put( requireNotBlankArgument( name, "name" ), requireNonNullArgument( value, "value" ) ); 356 357 //---* Done *---------------------------------------------------------- 358 return this; 359 } // set 360 361 /** 362 * {@inheritDoc} 363 */ 364 @Override 365 public final JSONObject set( final String name, final long value ) 366 { 367 return set( name, m_Builder.valueOf( value ) ); 368 } // set 369 370 /** 371 * {@inheritDoc} 372 */ 373 @Override 374 public final JSONObject set( final String name, final Long value ) 375 { 376 return set( name, m_Builder.valueOf( value ) ); 377 } // set 378 379 /** 380 * {@inheritDoc} 381 */ 382 @Override 383 public final JSONObject set( final String name, final String value ) 384 { 385 return set( name, m_Builder.valueOf( value ) ); 386 } // set 387 388 /** 389 * {@inheritDoc} 390 */ 391 @Override 392 public final JSONArray setArray( final String name ) 393 { 394 final var retValue = m_Builder.createArray(); 395 set( name, retValue ); 396 397 //---* Done *---------------------------------------------------------- 398 return retValue; 399 } // setArray() 400 401 /** 402 * {@inheritDoc} 403 */ 404 @Override 405 public final JSONObject setObject( final String name ) 406 { 407 final var retValue = m_Builder.createObject(); 408 set( name, retValue ); 409 410 //---* Done *---------------------------------------------------------- 411 return retValue; 412 } // setArray() 413 414 /** 415 * {@inheritDoc} 416 */ 417 @Override 418 public final int size() { return m_Members.size(); } 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public final String toString() 425 { 426 final var buffer = new StringJoiner( ",", "{", "}" ); 427 for( final var entry : m_Members.entrySet() ) 428 { 429 buffer.add( "%s:%s".formatted( new JSONStringImpl( entry.getKey() ).toString(), entry.getValue().toString() ) ); 430 } 431 final var retValue = buffer.toString(); 432 433 //---* Done *---------------------------------------------------------- 434 return retValue; 435 } // toString() 436} 437// class JSONObjectImpl 438 439/* 440 * End of File 441 */