001/******************************************************************************* 002 * Copyright (c) 2013, 2017 EclipseSource. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in all 012 * copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 020 * SOFTWARE. 021 ******************************************************************************/ 022package com.restfb.json; 023 024import java.io.IOException; 025import java.io.Serializable; 026import java.io.StringWriter; 027import java.io.Writer; 028import java.util.Objects; 029 030/** 031 * Represents a JSON value. This can be a JSON <strong>object</strong>, an <strong> array</strong>, a 032 * <strong>number</strong>, a <strong>string</strong>, or one of the literals 033 * <strong>true</strong>, <strong>false</strong>, and <strong>null</strong>. 034 * <p> 035 * The literals <strong>true</strong>, <strong>false</strong>, and <strong>null</strong> are 036 * represented by the constants {@link Json#TRUE}, {@link Json#FALSE}, and {@link Json#NULL}. 037 * </p> 038 * <p> 039 * JSON <strong>objects</strong> and <strong>arrays</strong> are represented by the subtypes 040 * {@link JsonObject} and {@link JsonArray}. Instances of these types can be created using the public constructors of these classes. 041 * </p> 042 * <p> 043 * Instances that represent JSON <strong>numbers</strong>, <strong>strings</strong> and 044 * <strong>boolean</strong> values can be created using the static factory methods 045 * {@link Json#value(String)}, {@link Json#value(long)}, {@link Json#value(double)}, etc. 046 * </p> 047 * <p> 048 * In order to find out whether an instance of this class is of a certain type, the methods 049 * {@link #isObject()}, {@link #isArray()}, {@link #isString()}, {@link #isNumber()} etc. can be used. 050 * </p> 051 * <p> 052 * If the type of a JSON value is known, the methods {@link #asObject()}, {@link #asArray()}, {@link #asString()}, 053 * {@link #asInt()}, etc. can be used to get this value directly in the appropriate target type. 054 * </p> 055 * <p> 056 * This class is <strong>not supposed to be extended</strong> by clients. 057 * </p> 058 */ 059@SuppressWarnings("serial") // use default serial UID 060public abstract class JsonValue implements Serializable { 061 062 // String constants 063 private static final String NOT_A_NUMBER = "Not a number: "; 064 private static final String NOT_A_STRING = "Not a string: "; 065 private static final String NOT_A_BOOLEAN = "Not a boolean: "; 066 // String constants 067 protected static final String STRING_IS_NULL = "string is null"; 068 protected static final String OBJECT_IS_NULL = "object is null"; 069 protected static final String NAME_IS_NULL = "name is null"; 070 protected static final String VALUE_IS_NULL = "value is null"; 071 protected static final String ARRAY_IS_NULL = "array is null"; 072 073 JsonValue() { 074 // prevent subclasses outside of this package 075 } 076 077 /** 078 * Detects whether this value represents a JSON object. If this is the case, this value is an instance of 079 * {@link JsonObject}. 080 * 081 * @return <code>true</code> if this value is an instance of JsonObject 082 */ 083 public boolean isObject() { 084 return false; 085 } 086 087 /** 088 * Detects whether this value represents a JSON array. If this is the case, this value is an instance of 089 * {@link JsonArray}. 090 * 091 * @return <code>true</code> if this value is an instance of JsonArray 092 */ 093 public boolean isArray() { 094 return false; 095 } 096 097 /** 098 * Detects whether this value represents a JSON number. 099 * 100 * @return <code>true</code> if this value represents a JSON number 101 */ 102 public boolean isNumber() { 103 return false; 104 } 105 106 /** 107 * Detects whether this value represents a JSON string. 108 * 109 * @return <code>true</code> if this value represents a JSON string 110 */ 111 public boolean isString() { 112 return false; 113 } 114 115 /** 116 * Detects whether this value represents a boolean value. 117 * 118 * @return <code>true</code> if this value represents either the JSON literal <code>true</code> or <code>false</code> 119 */ 120 public boolean isBoolean() { 121 return false; 122 } 123 124 /** 125 * Detects whether this value represents the JSON literal <code>true</code>. 126 * 127 * @return <code>true</code> if this value represents the JSON literal <code>true</code> 128 */ 129 public boolean isTrue() { 130 return false; 131 } 132 133 /** 134 * Detects whether this value represents the JSON literal <code>false</code>. 135 * 136 * @return <code>true</code> if this value represents the JSON literal <code>false</code> 137 */ 138 public boolean isFalse() { 139 return false; 140 } 141 142 /** 143 * Detects whether this value represents the JSON literal <code>null</code>. 144 * 145 * @return <code>true</code> if this value represents the JSON literal <code>null</code> 146 */ 147 public boolean isNull() { 148 return false; 149 } 150 151 /** 152 * Returns this JSON value as {@link JsonObject}, assuming that this value represents a JSON object. If this is not 153 * the case, an exception is thrown. 154 * 155 * @return a JSONObject for this value 156 * @throws UnsupportedOperationException 157 * if this value is not a JSON object 158 */ 159 public JsonObject asObject() { 160 throw new UnsupportedOperationException("Not an object: " + toString()); 161 } 162 163 /** 164 * Returns this JSON value as {@link JsonArray}, assuming that this value represents a JSON array. If this is not the 165 * case, an exception is thrown. 166 * 167 * @return a JSONArray for this value 168 * @throws UnsupportedOperationException 169 * if this value is not a JSON array 170 */ 171 public JsonArray asArray() { 172 throw new UnsupportedOperationException("Not an array: " + toString()); 173 } 174 175 /** 176 * Returns this JSON value as an <code>int</code> value, assuming that this value represents a JSON number that can be 177 * interpreted as Java <code>int</code>. If this is not the case, an exception is thrown. 178 * <p> 179 * To be interpreted as Java <code>int</code>, the JSON number must neither contain an exponent nor a fraction part. 180 * Moreover, the number must be in the <code>Integer</code> range. 181 * </p> 182 * 183 * @return this value as <code>int</code> 184 * @throws UnsupportedOperationException 185 * if this value is not a JSON number 186 * @throws NumberFormatException 187 * if this JSON number can not be interpreted as <code>int</code> value 188 */ 189 public int asInt() { 190 throw new UnsupportedOperationException(NOT_A_NUMBER + toString()); 191 } 192 193 /** 194 * Returns this JSON value as a <code>long</code> value, assuming that this value represents a JSON number that can be 195 * interpreted as Java <code>long</code>. If this is not the case, an exception is thrown. 196 * <p> 197 * To be interpreted as Java <code>long</code>, the JSON number must neither contain an exponent nor a fraction part. 198 * Moreover, the number must be in the <code>Long</code> range. 199 * </p> 200 * 201 * @return this value as <code>long</code> 202 * @throws UnsupportedOperationException 203 * if this value is not a JSON number 204 * @throws NumberFormatException 205 * if this JSON number can not be interpreted as <code>long</code> value 206 */ 207 public long asLong() { 208 throw new UnsupportedOperationException(NOT_A_NUMBER + toString()); 209 } 210 211 /** 212 * Returns this JSON value as a <code>float</code> value, assuming that this value represents a JSON number. If this 213 * is not the case, an exception is thrown. 214 * <p> 215 * If the JSON number is out of the <code>Float</code> range, {@link Float#POSITIVE_INFINITY} or 216 * {@link Float#NEGATIVE_INFINITY} is returned. 217 * </p> 218 * 219 * @return this value as <code>float</code> 220 * @throws UnsupportedOperationException 221 * if this value is not a JSON number 222 */ 223 public float asFloat() { 224 throw new UnsupportedOperationException(NOT_A_NUMBER + toString()); 225 } 226 227 /** 228 * Returns this JSON value as a <code>double</code> value, assuming that this value represents a JSON number. If this 229 * is not the case, an exception is thrown. 230 * <p> 231 * If the JSON number is out of the <code>Double</code> range, {@link Double#POSITIVE_INFINITY} or 232 * {@link Double#NEGATIVE_INFINITY} is returned. 233 * </p> 234 * 235 * @return this value as <code>double</code> 236 * @throws UnsupportedOperationException 237 * if this value is not a JSON number 238 */ 239 public double asDouble() { 240 throw new UnsupportedOperationException(NOT_A_NUMBER + toString()); 241 } 242 243 /** 244 * Returns this JSON value as String, assuming that this value represents a JSON string. If this is not the case, an 245 * exception is thrown. 246 * 247 * @return the string represented by this value 248 * @throws UnsupportedOperationException 249 * if this value is not a JSON string 250 */ 251 public String asString() { 252 throw new UnsupportedOperationException(NOT_A_STRING + toString()); 253 } 254 255 /** 256 * Returns this JSON value as a <code>boolean</code> value, assuming that this value is either <code>true</code> or 257 * <code>false</code>. If this is not the case, an exception is thrown. 258 * 259 * @return this value as <code>boolean</code> 260 * @throws UnsupportedOperationException 261 * if this value is neither <code>true</code> or <code>false</code> 262 */ 263 public boolean asBoolean() { 264 throw new UnsupportedOperationException(NOT_A_BOOLEAN + toString()); 265 } 266 267 /** 268 * Writes the JSON representation of this value to the given writer in its minimal form, without any additional 269 * whitespace. 270 * <p> 271 * Writing performance can be improved by using a {@link java.io.BufferedWriter BufferedWriter}. 272 * </p> 273 * 274 * @param writer 275 * the writer to write this value to 276 * @throws IOException 277 * if an I/O error occurs in the writer 278 */ 279 public void writeTo(Writer writer) throws IOException { 280 writeTo(writer, WriterConfig.MINIMAL); 281 } 282 283 /** 284 * Writes the JSON representation of this value to the given writer using the given formatting. 285 * <p> 286 * Writing performance can be improved by using a {@link java.io.BufferedWriter BufferedWriter}. 287 * </p> 288 * 289 * @param writer 290 * the writer to write this value to 291 * @param config 292 * a configuration that controls the formatting or <code>null</code> for the minimal form 293 * @throws IOException 294 * if an I/O error occurs in the writer 295 */ 296 public void writeTo(Writer writer, WriterConfig config) throws IOException { 297 Objects.requireNonNull(writer, "writer is null"); 298 Objects.requireNonNull(config, "config is null"); 299 WritingBuffer buffer = new WritingBuffer(writer, 128); 300 write(config.createWriter(buffer)); 301 buffer.flush(); 302 } 303 304 /** 305 * Returns the JSON string for this value in its minimal form, without any additional whitespace. 306 * The result is guaranteed to be a valid input for the method {@link Json#parse(String)} and to 307 * create a value that is <em>equal</em> to this object. 308 * 309 * @return a JSON string that represents this value 310 */ 311 @Override 312 public String toString() { 313 return toString(WriterConfig.UNICODE); 314 } 315 316 /** 317 * Returns the JSON string for this value using the given formatting. 318 * 319 * @param config 320 * a configuration that controls the formatting or <code>null</code> for the minimal form 321 * @return a JSON string that represents this value 322 */ 323 public String toString(WriterConfig config) { 324 StringWriter writer = new StringWriter(); 325 try { 326 writeTo(writer, config); 327 } catch (IOException exception) { 328 // StringWriter does not throw IOExceptions 329 throw new RuntimeException(exception); 330 } 331 return writer.toString(); 332 } 333 334 /** 335 * Indicates whether some other object is "equal to" this one according to the contract specified in 336 * {@link Object#equals(Object)}. 337 * <p> 338 * Two JsonValues are considered equal if and only if they represent the same JSON text. As a consequence, two given 339 * JsonObjects may be different even though they contain the same set of names with the same values, but in a 340 * different order. 341 * </p> 342 * 343 * @param object 344 * the reference object with which to compare 345 * @return true if this object is the same as the object argument; false otherwise 346 */ 347 @Override 348 public boolean equals(Object object) { 349 return super.equals(object); 350 } 351 352 @Override 353 public int hashCode() { 354 return super.hashCode(); 355 } 356 357 abstract void write(JsonWriter writer) throws IOException; 358 359}