Skip to content

Commit bd5981c

Browse files
committed
Unsafe JSONObject update upgrade
update method upgraded for JSONObject arg
1 parent 484f388 commit bd5981c

File tree

3 files changed

+274
-30
lines changed

3 files changed

+274
-30
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>com.github.leonardofel</groupId>
55
<artifactId>json-java-put-null-fix</artifactId>
6-
<version>3.0.36.unsafe</version>
6+
<version>3.0.37.unsafe</version>
77
<packaging>jar</packaging>
88

99
<name>JSON in Java WITH WORKING .put(null)</name>

src/main/java/org/json/JSONObject.java

Lines changed: 126 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ of this software and associated documentation files (the "Software"), to deal
4444
import java.util.Iterator;
4545
import java.util.Locale;
4646
import java.util.Map;
47+
import java.util.Objects;
4748
import java.util.ResourceBundle;
4849
import java.util.Set;
49-
import java.util.function.BiFunction;
50-
import java.util.function.Supplier;
5150
import java.util.regex.Pattern;
5251

5352
/**
@@ -1794,19 +1793,137 @@ public JSONObject putMap(String key, Map<?, ?> value) throws JSONException {
17941793
}
17951794

17961795
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
1797-
private final String propertyName = "map";
1798-
public void addUpdateListener(PropertyChangeListener propertyChangeListener) {
1799-
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
1796+
/**
1797+
* Add a PropertyChangeListener to the JSONObject.
1798+
* The listener object may be added more than once, and will be called
1799+
* as many times as it is added.
1800+
* If {@code listener} is null, no exception is thrown and no action
1801+
* is taken.
1802+
*
1803+
* @param listener The PropertyChangeListener to be added
1804+
*/
1805+
public void addUpdateListenerGlobal(PropertyChangeListener listener) {
1806+
this.propertyChangeSupport.addPropertyChangeListener(listener);
1807+
}
1808+
1809+
/**
1810+
* Add a PropertyChangeListener for a specific property to the JSONObject.
1811+
* The listener will be invoked only when a call on
1812+
* update names that specific property
1813+
* The listener object may be added more than once, and will be called
1814+
* as many times as it is added.
1815+
* If {@code listener} is null, no exception is thrown and no action
1816+
* is taken.
1817+
*
1818+
* @param key The key string to listen on.
1819+
* @param listener The PropertyChangeListener to be added
1820+
*/
1821+
public void addUpdateListener(String key, PropertyChangeListener listener) {
1822+
this.propertyChangeSupport.addPropertyChangeListener(key, listener);
18001823
}
18011824

1802-
public void update(String key, Object newValue) throws JSONException {
1803-
if (propertyChangeSupport.hasListeners(this.propertyName)) {
1825+
/**
1826+
* Put a key/value pair in the JSONObject and
1827+
* reports a bound property update to listeners.
1828+
*
1829+
* @param key
1830+
* A key string.
1831+
* @param newValue
1832+
* An object which is the newValue. It should be of one of these
1833+
* types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
1834+
* String, or the JSONObject.NULL object.
1835+
* @return this.
1836+
* @throws JSONException
1837+
* If the newValue is non-finite number.
1838+
* @throws JSONException
1839+
* If updateListener not initialized.
1840+
*/
1841+
public JSONObject update(String key, Object newValue) throws JSONException {
1842+
if (this.propertyChangeSupport.hasListeners(key)) {
18041843
final Object oldValue = this.opt(key);
18051844
this.put(key, newValue);
1806-
propertyChangeSupport.firePropertyChange(this.propertyName, oldValue, newValue);
1845+
1846+
this.propertyChangeSupport.firePropertyChange(key, oldValue, newValue);
18071847
} else {
1808-
throw new JSONException("updateListener not initialized");
1848+
throw new JSONException("updateListener on \"" + key + "\" not initialized");
1849+
}
1850+
1851+
return this;
1852+
}
1853+
1854+
/**
1855+
* Put a key/value pair in the JSONObject and
1856+
* reports a bound property update to listeners.
1857+
*
1858+
* @param key
1859+
* A key string.
1860+
* @param newValue
1861+
* An object which is the newValue. It should be of one of these
1862+
* types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
1863+
* String, or the JSONObject.NULL object.
1864+
* @return this.
1865+
* @throws JSONException
1866+
* If the newValue is non-finite number.
1867+
* @throws JSONException
1868+
* If updateListener not initialized.
1869+
*/
1870+
public JSONObject update(JSONObject jo) throws JSONException {
1871+
return this.updateOrRemove(jo, false);
1872+
}
1873+
1874+
/**
1875+
* Put a key/value pair in the JSONObject and
1876+
* reports a bound property update to listeners.
1877+
*
1878+
* @param key
1879+
* A key string.
1880+
* @param newValue
1881+
* An object which is the newValue. It should be of one of these
1882+
* types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
1883+
* String, or the JSONObject.NULL object.
1884+
* @return this.
1885+
* @throws JSONException
1886+
* If the newValue is non-finite number.
1887+
* @throws JSONException
1888+
* If updateListener not initialized.
1889+
*/
1890+
public JSONObject updateOrRemove(JSONObject jo) throws JSONException {
1891+
return this.updateOrRemove(jo, true);
1892+
}
1893+
1894+
private JSONObject updateOrRemove(JSONObject jo, boolean remove) throws JSONException {
1895+
final HashMap<String, Map.Entry<Object, Object>> entries = new HashMap<String, Map.Entry<Object, Object>>();
1896+
1897+
jo.forEach((key, newValue) -> {
1898+
this.merge(key, newValue, (v1, v2) -> {
1899+
if (Objects.equals(v1, v2)) {
1900+
return v1;
1901+
} else {
1902+
entries.put(key, Map.entry(v1, v2));
1903+
return v2;
1904+
}
1905+
});
1906+
});
1907+
1908+
if (remove) {
1909+
this.forEach((key, oldValue) -> {
1910+
if (!jo.has(key)) {
1911+
this.remove(key);
1912+
final Object ret = entries.put(key, Map.entry(oldValue, null));
1913+
if (ret != null) {
1914+
System.err.println("unexpected behavior!");
1915+
}
1916+
}
1917+
});
18091918
}
1919+
1920+
entries.forEach((key, entry) -> {
1921+
final Object oldValue = entry.getKey();
1922+
final Object newValue = entry.getValue();
1923+
this.propertyChangeSupport.firePropertyChange(key, oldValue, newValue);
1924+
});
1925+
1926+
return this;
18101927
}
18111928

18121929
/**

src/test/java/org/json/junit/JSONTest.java

Lines changed: 147 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package org.json.junit;
22

33
import static org.junit.Assert.assertEquals;
4-
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertNotEquals;
5+
import static org.junit.Assert.assertNotNull;
6+
import static org.junit.Assert.assertNull;
7+
import static org.junit.Assert.assertThrows;
58
import static org.junit.Assert.assertTrue;
69
import static org.junit.Assert.fail;
710

8-
import java.math.BigDecimal;
9-
import java.math.BigInteger;
1011
import java.util.Collections;
1112
import java.util.HashMap;
1213
import java.util.Map;
13-
import java.util.concurrent.atomic.AtomicBoolean;
14+
import java.util.concurrent.atomic.AtomicInteger;
1415

16+
import org.json.JSONException;
1517
import org.json.JSONObject;
1618
import org.junit.Test;
1719

@@ -397,14 +399,14 @@ public void testPutFloat() {
397399

398400
@Test
399401
public void lastTest() {
400-
final Byte a_Byte /* = new JSONObject().optByte("a") */;
401-
final Double a_Double = new JSONObject().optDouble("a"); //testNullDouble
402-
final Float a_Float = new JSONObject().optFloat("a"); //testNullFloat
403-
final Integer a_Integer = new JSONObject().optInteger("a"); //testNullInteger
404-
final Long a_Long = new JSONObject().optLong("a"); //testNullLong
405-
final Short a_Short /* = new JSONObject().getShort("a") */;
406-
final BigDecimal a_BigDecimal /* = new JSONObject().getBigDecimal("a") */;
407-
final BigInteger a_BigInteger /* = new JSONObject().getBigInteger("a") */;
402+
//final Byte a_Byte /* = new JSONObject().optByte("a") */;
403+
//final Double a_Double = new JSONObject().optDouble("a"); //testNullDouble
404+
//final Float a_Float = new JSONObject().optFloat("a"); //testNullFloat
405+
//final Integer a_Integer = new JSONObject().optInteger("a"); //testNullInteger
406+
//final Long a_Long = new JSONObject().optLong("a"); //testNullLong
407+
//final Short a_Short /* = new JSONObject().getShort("a") */;
408+
//final BigDecimal a_BigDecimal /* = new JSONObject().getBigDecimal("a") */;
409+
//final BigInteger a_BigInteger /* = new JSONObject().getBigInteger("a") */;
408410
}
409411

410412
@Test
@@ -420,20 +422,145 @@ public void computeTest() {
420422
assertEquals(r2, "myNull");
421423
}
422424

423-
424425
@Test
425-
public void updateTest() {
426+
public void updateNotEqualsTest() {
426427
final JSONObject j = new JSONObject();
427-
final AtomicBoolean atomicBoolean = new AtomicBoolean();
428-
assertFalse(atomicBoolean.get());
429-
j.addUpdateListener(evt -> {
428+
429+
assertThrows(JSONException.class, () -> j.update("myMapListener", "propertyChange"));
430+
431+
j.addUpdateListenerGlobal(evt -> {
430432
final Object oldValue = evt.getOldValue();
431433
assertEquals(oldValue, null);
434+
});
435+
436+
j.addUpdateListenerGlobal(evt -> {
437+
final Object newValue = evt.getNewValue();
438+
assertTrue(newValue.toString().startsWith("propertyChange"));
439+
});
440+
441+
j.addUpdateListener("myMapListener", evt -> {
442+
final Object oldValue = evt.getOldValue();
432443
final Object newValue = evt.getNewValue();
433-
assertEquals(newValue, "propertyChange");
434-
atomicBoolean.set(true);
444+
445+
assertNotEquals(oldValue, newValue);
435446
});
447+
436448
j.update("myMapListener", "propertyChange");
437-
assertTrue(atomicBoolean.get());
449+
}
450+
451+
@Test
452+
public void updateListenerGlobalTest() {
453+
final JSONObject j = new JSONObject();
454+
455+
final AtomicInteger counter = new AtomicInteger();
456+
final AtomicInteger globalExecutions = new AtomicInteger();
457+
assertEquals(counter.get(), globalExecutions.get());
458+
459+
j.addUpdateListenerGlobal(evt -> {
460+
globalExecutions.incrementAndGet();
461+
});
462+
463+
j.addUpdateListenerGlobal(evt -> {
464+
assertEquals(counter.incrementAndGet(), globalExecutions.get() * 3 - 2);
465+
});
466+
467+
j.addUpdateListenerGlobal(evt -> {
468+
assertEquals(counter.incrementAndGet(), globalExecutions.get() * 3 - 1);
469+
});
470+
471+
j.addUpdateListener("myMapListener", evt -> {
472+
assertEquals(counter.incrementAndGet(), globalExecutions.get() * 3 - 0);
473+
});
474+
475+
j.update("myMapListener", "propertyChange123");
476+
j.update("myMapListener", "propertyChange456");
477+
j.update("myMapListener", "propertyChange789");
478+
479+
assertEquals(counter.get(), globalExecutions.get() * 3);
480+
}
481+
482+
@Test
483+
public void updateListenerTest() {
484+
final JSONObject j = new JSONObject();
485+
486+
j.put("myMapListener", "unchangedProperty");
487+
488+
j.addUpdateListener("myMapListener", evt -> {
489+
fail("They are the same");
490+
});
491+
492+
j.update("myMapListener", "unchangedProperty");
493+
494+
j.addUpdateListener("otherMapListener", evt -> {
495+
final Object oldValue = evt.getOldValue();
496+
assertEquals(oldValue, null);
497+
498+
final Object newValue = evt.getNewValue();
499+
assertEquals(newValue, "otherOtherPropertyChange");
500+
});
501+
502+
j.update("otherMapListener", "otherOtherPropertyChange");
503+
}
504+
505+
@Test
506+
public void updateListener2Test() {
507+
final JSONObject jsonObject1 = new JSONObject();
508+
final JSONObject jsonObject2 = new JSONObject();
509+
510+
jsonObject1
511+
.put("trueKey", Boolean.valueOf(true))
512+
.put("falseKey", Boolean.valueOf(false))
513+
.put("stringKey", "CHANGE ME!!!");
514+
//.put("nullKey", "NOT NULL");
515+
516+
final JSONObject oldJsonObject1 = new JSONObject(jsonObject1.toString());
517+
518+
jsonObject2
519+
.put("stringKey", "hello world!")
520+
.put("nullKey", null)
521+
.put("escapeStringKey", "h\be\tllo w\u1234orld!")
522+
.put("intKey", Long.valueOf(42));
523+
//.put("doubleKey", Double.valueOf(-23.45e67)); PROBLEM WITH DOUBLE CONVERTING TO BIGDECIMAL AFTER JSONOBJECT.TOSTRING
524+
525+
final JSONObject oldJsonObject2 = new JSONObject(jsonObject2.toString());
526+
527+
jsonObject1.addUpdateListenerGlobal(evt -> {
528+
final Object oldValue = evt.getOldValue();
529+
final Object newValue = evt.getNewValue();
530+
531+
assertNotEquals(oldValue, newValue);
532+
});
533+
534+
jsonObject1.addUpdateListener("trueKey", evt -> {
535+
assertNull(evt.getOldValue());
536+
assertTrue("expected \"trueKey\":true", Boolean.TRUE.equals(evt.getNewValue()));
537+
});
538+
jsonObject1.addUpdateListener("falseKey", evt -> {
539+
assertNull(evt.getOldValue());
540+
assertTrue("expected \"falseKey\":false", Boolean.FALSE.equals(evt.getNewValue()));
541+
});
542+
jsonObject1.addUpdateListener("stringKey", evt -> {
543+
assertNotNull(evt.getOldValue());
544+
assertTrue("expected \"stringKey\":\"hello world!\"", "hello world!".equals(evt.getNewValue()));
545+
});
546+
jsonObject1.addUpdateListener("nullKey", evt -> {
547+
fail("They are the same");
548+
});
549+
jsonObject1.addUpdateListener("escapeStringKey", evt -> {
550+
assertNotNull(evt.getOldValue());
551+
assertTrue("expected \"escapeStringKey\":\"h\be\tllo w\u1234orld!\"", "h\be\tllo w\u1234orld!".equals(evt.getNewValue()));
552+
});
553+
jsonObject1.addUpdateListener("intKey", evt -> {
554+
assertNotNull(evt.getOldValue());
555+
assertTrue("expected \"intKey\":42", Long.valueOf("42").equals(evt.getNewValue()));
556+
});
557+
558+
assertEquals(jsonObject1.toString(), oldJsonObject1.toString());
559+
assertEquals(jsonObject2.toString(), oldJsonObject2.toString());
560+
561+
jsonObject1.update(jsonObject2);
562+
563+
assertNotEquals(jsonObject1.toString(), oldJsonObject1.toString());
564+
assertEquals(jsonObject2.toString(), oldJsonObject2.toString());
438565
}
439566
}

0 commit comments

Comments
 (0)