AbstractJSON.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2006-2020 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     https://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package org.kordamp.json;
019 
020 import org.kordamp.json.util.JSONUtils;
021 import org.kordamp.json.util.JsonEventListener;
022 import org.slf4j.Logger;
023 import org.slf4j.LoggerFactory;
024 
025 import java.io.IOException;
026 import java.io.Writer;
027 import java.lang.ref.SoftReference;
028 import java.util.Collection;
029 import java.util.HashSet;
030 import java.util.Iterator;
031 import java.util.Set;
032 import java.util.TreeSet;
033 
034 /**
035  * Base class for JSONObject and JSONArray.
036  *
037  @author Andres Almiray
038  */
039 abstract class AbstractJSON implements JSON {
040     private static final Logger LOG = LoggerFactory.getLogger(AbstractJSON.class);
041     private static final WritingVisitor NORMAL = new WritingVisitor() {
042         public Collection keySet(JSONObject o) {
043             return o.keySet();
044         }
045 
046         public void on(JSON o, Writer wthrows IOException {
047             o.write(w);
048         }
049 
050         public void on(Object value, Writer wthrows IOException {
051             w.write(JSONUtils.valueToString(value));
052         }
053     };
054     private static final WritingVisitor CANONICAL = new WritingVisitor() {
055         public Collection keySet(JSONObject o) {
056             return new TreeSet(o.keySet())// sort them alphabetically
057         }
058 
059         public void on(JSON o, Writer wthrows IOException {
060             o.writeCanonical(w);
061         }
062 
063         public void on(Object value, Writer wthrows IOException {
064             w.write(JSONUtils.valueToCanonicalString(value));
065         }
066     };
067     private static CycleSet cycleSet = new CycleSet();
068 
069     /**
070      * Adds a reference for cycle detection check.
071      *
072      @param instance the reference to add
073      *
074      @return true if the instance has not been added previously, false
075      * otherwise.
076      */
077     protected static boolean addInstance(Object instance) {
078         return getCycleSet().add(instance);
079     }
080 
081     /**
082      * Fires an end of array event.
083      */
084     protected static void fireArrayEndEvent(JsonConfig jsonConfig) {
085         if (jsonConfig.isEventTriggeringEnabled()) {
086             for (Iterator listeners = jsonConfig.getJsonEventListeners()
087                 .iterator(); listeners.hasNext()) {
088                 JsonEventListener listener = (JsonEventListenerlisteners.next();
089                 try {
090                     listener.onArrayEnd();
091                 catch (RuntimeException e) {
092                     LOG.warn(e.getMessage(), e);
093                 }
094             }
095         }
096     }
097 
098     /**
099      * Fires a start of array event.
100      */
101     protected static void fireArrayStartEvent(JsonConfig jsonConfig) {
102         if (jsonConfig.isEventTriggeringEnabled()) {
103             for (Iterator listeners = jsonConfig.getJsonEventListeners()
104                 .iterator(); listeners.hasNext()) {
105                 JsonEventListener listener = (JsonEventListenerlisteners.next();
106                 try {
107                     listener.onArrayStart();
108                 catch (RuntimeException e) {
109                     LOG.warn(e.getMessage(), e);
110                 }
111             }
112         }
113     }
114 
115     /**
116      * Fires an element added event.
117      *
118      @param index   the index where the element was added
119      @param element the added element
120      */
121     protected static void fireElementAddedEvent(int index, Object element, JsonConfig jsonConfig) {
122         if (jsonConfig.isEventTriggeringEnabled()) {
123             for (Iterator listeners = jsonConfig.getJsonEventListeners()
124                 .iterator(); listeners.hasNext()) {
125                 JsonEventListener listener = (JsonEventListenerlisteners.next();
126                 try {
127                     listener.onElementAdded(index, element);
128                 catch (RuntimeException e) {
129                     LOG.warn(e.getMessage(), e);
130                 }
131             }
132         }
133     }
134 
135     /**
136      * Fires an error event.
137      *
138      @param jsone the thrown exception
139      */
140     protected static void fireErrorEvent(JSONException jsone, JsonConfig jsonConfig) {
141         if (jsonConfig.isEventTriggeringEnabled()) {
142             for (Iterator listeners = jsonConfig.getJsonEventListeners()
143                 .iterator(); listeners.hasNext()) {
144                 JsonEventListener listener = (JsonEventListenerlisteners.next();
145                 try {
146                     listener.onError(jsone);
147                 catch (RuntimeException e) {
148                     LOG.warn(e.getMessage(), e);
149                 }
150             }
151         }
152     }
153 
154     /**
155      * Fires an end of object event.
156      */
157     protected static void fireObjectEndEvent(JsonConfig jsonConfig) {
158         if (jsonConfig.isEventTriggeringEnabled()) {
159             for (Iterator listeners = jsonConfig.getJsonEventListeners()
160                 .iterator(); listeners.hasNext()) {
161                 JsonEventListener listener = (JsonEventListenerlisteners.next();
162                 try {
163                     listener.onObjectEnd();
164                 catch (RuntimeException e) {
165                     LOG.warn(e.getMessage(), e);
166                 }
167             }
168         }
169     }
170 
171     /**
172      * Fires a start of object event.
173      */
174     protected static void fireObjectStartEvent(JsonConfig jsonConfig) {
175         if (jsonConfig.isEventTriggeringEnabled()) {
176             for (Iterator listeners = jsonConfig.getJsonEventListeners()
177                 .iterator(); listeners.hasNext()) {
178                 JsonEventListener listener = (JsonEventListenerlisteners.next();
179                 try {
180                     listener.onObjectStart();
181                 catch (RuntimeException e) {
182                     LOG.warn(e.getMessage(), e);
183                 }
184             }
185         }
186     }
187 
188     /**
189      * Fires a property set event.
190      *
191      @param key         the name of the property
192      @param value       the value of the property
193      @param accumulated if the value has been accumulated over 'key'
194      */
195     protected static void firePropertySetEvent(String key, Object value, boolean accumulated,
196                                                JsonConfig jsonConfig) {
197         if (jsonConfig.isEventTriggeringEnabled()) {
198             for (Iterator listeners = jsonConfig.getJsonEventListeners()
199                 .iterator(); listeners.hasNext()) {
200                 JsonEventListener listener = (JsonEventListenerlisteners.next();
201                 try {
202                     listener.onPropertySet(key, value, accumulated);
203                 catch (RuntimeException e) {
204                     LOG.warn(e.getMessage(), e);
205                 }
206             }
207         }
208     }
209 
210     /**
211      * Fires a warning event.
212      *
213      @param warning the warning message
214      */
215     protected static void fireWarnEvent(String warning, JsonConfig jsonConfig) {
216         if (jsonConfig.isEventTriggeringEnabled()) {
217             for (Iterator listeners = jsonConfig.getJsonEventListeners()
218                 .iterator(); listeners.hasNext()) {
219                 JsonEventListener listener = (JsonEventListenerlisteners.next();
220                 try {
221                     listener.onWarning(warning);
222                 catch (RuntimeException e) {
223                     LOG.warn(e.getMessage(), e);
224                 }
225             }
226         }
227     }
228 
229     /**
230      * Removes a reference for cycle detection check.
231      */
232     protected static void removeInstance(Object instance) {
233         Set set = getCycleSet();
234         set.remove(instance);
235         if (set.size() == 0) {
236             cycleSet.remove();
237         }
238     }
239 
240     private static Set getCycleSet() {
241         return cycleSet.getSet();
242     }
243 
244     protected Object _processValue(Object value, JsonConfig jsonConfig) {
245         if (JSONNull.getInstance().equals(value)) {
246             return JSONNull.getInstance();
247         else if (Class.class.isAssignableFrom(value.getClass()) || value instanceof Class) {
248             return ((Classvalue).getName();
249         else if (value instanceof JSONFunction) {
250             return value;
251         else if (value instanceof JSONString) {
252             return JSONSerializer.toJSON((JSONStringvalue, jsonConfig);
253         else if (value instanceof JSON) {
254             return JSONSerializer.toJSON(value, jsonConfig);
255         else if (JSONUtils.isArray(value)) {
256             return JSONArray.fromObject(value, jsonConfig);
257         else if (JSONUtils.isString(value)) {
258             return value.toString();
259         else if (JSONUtils.isNumber(value)) {
260             JSONUtils.testValidity(value);
261             return JSONUtils.transformNumber((Numbervalue);
262         else if (JSONUtils.isBoolean(value)) {
263             return value;
264         else {
265             JSONObject jsonObject = JSONObject.fromObject(value, jsonConfig);
266             if (jsonObject.isNullObject()) {
267                 return JSONNull.getInstance();
268             else {
269                 return jsonObject;
270             }
271         }
272     }
273 
274     public final Writer write(Writer writerthrows IOException {
275         write(writer, NORMAL);
276         return writer;
277     }
278 
279     public final Writer writeCanonical(Writer writerthrows IOException {
280         write(writer, CANONICAL);
281         return writer;
282     }
283 
284     protected abstract void write(Writer w, WritingVisitor vthrows IOException;
285 
286     interface WritingVisitor {
287         Collection keySet(JSONObject o);
288 
289         void on(JSON o, Writer wthrows IOException;
290 
291         void on(Object value, Writer wthrows IOException;
292     }
293 
294     private static class CycleSet extends ThreadLocal {
295         protected Object initialValue() {
296             return new SoftReference(new HashSet());
297         }
298 
299         public Set getSet() {
300             Set set = (Set) ((SoftReferenceget()).get();
301             if (set == null) {
302                 set = new HashSet();
303                 set(new SoftReference(set));
304             }
305             return set;
306         }
307     }
308 }