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 w) throws IOException {
047 o.write(w);
048 }
049
050 public void on(Object value, Writer w) throws 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 w) throws IOException {
060 o.writeCanonical(w);
061 }
062
063 public void on(Object value, Writer w) throws 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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 = (JsonEventListener) listeners.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 ((Class) value).getName();
249 } else if (value instanceof JSONFunction) {
250 return value;
251 } else if (value instanceof JSONString) {
252 return JSONSerializer.toJSON((JSONString) value, 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((Number) value);
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 writer) throws IOException {
275 write(writer, NORMAL);
276 return writer;
277 }
278
279 public final Writer writeCanonical(Writer writer) throws IOException {
280 write(writer, CANONICAL);
281 return writer;
282 }
283
284 protected abstract void write(Writer w, WritingVisitor v) throws IOException;
285
286 interface WritingVisitor {
287 Collection keySet(JSONObject o);
288
289 void on(JSON o, Writer w) throws IOException;
290
291 void on(Object value, Writer w) throws 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) ((SoftReference) get()).get();
301 if (set == null) {
302 set = new HashSet();
303 set(new SoftReference(set));
304 }
305 return set;
306 }
307 }
308 }
|