Coverage Report - org.apache.tapestry5.json.JSONArray
 
Classes in this File Line Coverage Branch Coverage Complexity
JSONArray
98%
86/88
83%
50/60
0
 
 1  
 // Copyright 2007, 2008 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry5.json;
 16  
 
 17  
 /*
 18  
  Copyright (c) 2002 JSON.org
 19  
 
 20  
  Permission is hereby granted, free of charge, to any person obtaining a copy
 21  
  of this software and associated documentation files (the "Software"), to deal
 22  
  in the Software without restriction, including without limitation the rights
 23  
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 24  
  copies of the Software, and to permit persons to whom the Software is
 25  
  furnished to do so, subject to the following conditions:
 26  
 
 27  
  The above copyright notice and this permission notice shall be included in all
 28  
  copies or substantial portions of the Software.
 29  
 
 30  
  The Software shall be used for Good, not Evil.
 31  
 
 32  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 33  
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 34  
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 35  
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 36  
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 37  
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 38  
  SOFTWARE.
 39  
  */
 40  
 
 41  
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 42  
 
 43  
 import java.util.List;
 44  
 
 45  
 /**
 46  
  * A JSONArray is an ordered sequence of values. Its external text form is a string wrapped in square brackets with
 47  
  * commas separating the values. The internal form is an object having <code>get</code> and <code>opt</code> methods for
 48  
  * accessing the values by index, and <code>put</code> methods for adding or replacing values. The values can be any of
 49  
  * these types: <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
 50  
  * <code>String</code>, or the <code>JSONObject.NULL object</code>.
 51  
  * <p/>
 52  
  * The constructor can convert a JSON text into a Java object. The <code>toString</code> method converts to JSON text.
 53  
  * <p/>
 54  
  * A <code>get</code> method returns a value if one can be found, and throws an exception if one cannot be found. An
 55  
  * <code>opt</code> method returns a default value instead of throwing an exception, and so is useful for obtaining
 56  
  * optional values.
 57  
  * <p/>
 58  
  * The generic <code>get()</code> and <code>opt()</code> methods return an object which you can cast or query for type.
 59  
  * There are also typed <code>get</code> and <code>opt</code> methods that do type checking and type coersion for you.
 60  
  * <p/>
 61  
  * The texts produced by the <code>toString</code> methods strictly conform to JSON syntax rules. The constructors are
 62  
  * more forgiving in the texts they will accept: <ul> <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear
 63  
  * just before the closing bracket.</li> <li>The <code>null</code> value will be inserted when there is
 64  
  * <code>,</code>&nbsp;<small>(comma)</small> elision.</li> <li>Strings may be quoted with
 65  
  * <code>'</code>&nbsp;<small>(single quote)</small>.</li> <li>Strings do not need to be quoted at all if they do not
 66  
  * begin with a quote or single quote, and if they do not contain leading or trailing spaces, and if they do not contain
 67  
  * any of these characters: <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and if they are not
 68  
  * the reserved words <code>true</code>, <code>false</code>, or <code>null</code>.</li> <li>Values can be separated by
 69  
  * <code>;</code> <small>(semicolon)</small> as well as by <code>,</code> <small>(comma)</small>.</li> <li>Numbers may
 70  
  * have the <code>0-</code> <small>(octal)</small> or <code>0x-</code> <small>(hex)</small> prefix.</li> <li>Comments
 71  
  * written in the slashshlash, slashstar, and hash conventions will be ignored.</li> </ul>
 72  
  *
 73  
  * @author JSON.org
 74  
  * @version 2
 75  
  */
 76  2
 public final class JSONArray
 77  
 {
 78  
 
 79  
     /**
 80  
      * The arrayList where the JSONArray's properties are kept.
 81  
      */
 82  2220
     private final List<Object> list = CollectionFactory.newList();
 83  
 
 84  
     /**
 85  
      * Construct an empty JSONArray.
 86  
      */
 87  
     public JSONArray()
 88  2144
     {
 89  2144
     }
 90  
 
 91  
     public JSONArray(String text)
 92  26
     {
 93  26
         JSONTokener tokener = new JSONTokener(text);
 94  
 
 95  26
         parse(tokener);
 96  24
     }
 97  
 
 98  
     public JSONArray(Object... values)
 99  46
     {
 100  46
         for (Object value : values) put(value);
 101  46
     }
 102  
 
 103  
     /**
 104  
      * Construct a JSONArray from a JSONTokener.
 105  
      *
 106  
      * @param tokenizer A JSONTokener
 107  
      * @throws RuntimeException If there is a syntax error.
 108  
      */
 109  
     JSONArray(JSONTokener tokenizer)
 110  4
     {
 111  4
         assert tokenizer != null;
 112  
 
 113  4
         parse(tokenizer);
 114  2
     }
 115  
 
 116  
     private void parse(JSONTokener tokenizer)
 117  
     {
 118  30
         if (tokenizer.nextClean() != '[')
 119  
         {
 120  2
             throw tokenizer
 121  
                     .syntaxError("A JSONArray text must start with '['");
 122  
         }
 123  
 
 124  28
         if (tokenizer.nextClean() == ']')
 125  
         {
 126  2
             return;
 127  
         }
 128  
 
 129  26
         tokenizer.back();
 130  
 
 131  
         while (true)
 132  
         {
 133  92
             if (tokenizer.nextClean() == ',')
 134  
             {
 135  2
                 tokenizer.back();
 136  2
                 list.add(JSONObject.NULL);
 137  
             }
 138  
             else
 139  
             {
 140  90
                 tokenizer.back();
 141  90
                 list.add(tokenizer.nextValue());
 142  
             }
 143  
 
 144  92
             switch (tokenizer.nextClean())
 145  
             {
 146  
                 case ';':
 147  
                 case ',':
 148  68
                     if (tokenizer.nextClean() == ']')
 149  
                     {
 150  2
                         return;
 151  
                     }
 152  66
                     tokenizer.back();
 153  66
                     break;
 154  
 
 155  
                 case ']':
 156  22
                     return;
 157  
 
 158  
                 default:
 159  2
                     throw tokenizer.syntaxError("Expected a ',' or ']'");
 160  
             }
 161  
         }
 162  
     }
 163  
 
 164  
     /**
 165  
      * Get the object value associated with an index.
 166  
      *
 167  
      * @param index The index must be between 0 and length() - 1.
 168  
      * @return An object value.
 169  
      * @throws RuntimeException If there is no value for the index.
 170  
      */
 171  
     public Object get(int index)
 172  
     {
 173  106
         return list.get(index);
 174  
     }
 175  
 
 176  
     /**
 177  
      * Get the boolean value associated with an index. The string values "true" and "false" are converted to boolean.
 178  
      *
 179  
      * @param index The index must be between 0 and length() - 1.
 180  
      * @return The truth.
 181  
      * @throws RuntimeException If there is no value for the index or if the value is not convertable to boolean.
 182  
      */
 183  
     public boolean getBoolean(int index)
 184  
     {
 185  10
         Object value = get(index);
 186  
 
 187  10
         if (value instanceof Boolean)
 188  
         {
 189  4
             return (Boolean) value;
 190  
         }
 191  
 
 192  6
         if (value instanceof String)
 193  
         {
 194  6
             String asString = (String) value;
 195  
 
 196  6
             if (asString.equalsIgnoreCase("false")) return false;
 197  
 
 198  4
             if (asString.equalsIgnoreCase("true")) return true;
 199  
         }
 200  
 
 201  2
         throw new RuntimeException("JSONArray[" + index + "] is not a Boolean.");
 202  
     }
 203  
 
 204  
     /**
 205  
      * Get the double value associated with an index.
 206  
      *
 207  
      * @param index The index must be between 0 and length() - 1.
 208  
      * @return The value.
 209  
      * @throws IllegalArgumentException If the key is not found or if the value cannot be converted to a number.
 210  
      */
 211  
     public double getDouble(int index)
 212  
     {
 213  8
         Object value = get(index);
 214  
 
 215  
         try
 216  
         {
 217  8
             if (value instanceof Number) return ((Number) value).doubleValue();
 218  
 
 219  6
             return Double.valueOf((String) value);
 220  
         }
 221  2
         catch (Exception e)
 222  
         {
 223  2
             throw new IllegalArgumentException("JSONArray[" + index + "] is not a number.");
 224  
         }
 225  
     }
 226  
 
 227  
     /**
 228  
      * Get the int value associated with an index.
 229  
      *
 230  
      * @param index The index must be between 0 and length() - 1.
 231  
      * @return The value.
 232  
      * @throws IllegalArgumentException If the key is not found or if the value cannot be converted to a number. if the
 233  
      *                                  value cannot be converted to a number.
 234  
      */
 235  
     public int getInt(int index)
 236  
     {
 237  8
         Object o = get(index);
 238  8
         return o instanceof Number ? ((Number) o).intValue() : (int) getDouble(index);
 239  
     }
 240  
 
 241  
     /**
 242  
      * Get the JSONArray associated with an index.
 243  
      *
 244  
      * @param index The index must be between 0 and length() - 1.
 245  
      * @return A JSONArray value.
 246  
      * @throws RuntimeException If there is no value for the index. or if the value is not a JSONArray
 247  
      */
 248  
     public JSONArray getJSONArray(int index)
 249  
     {
 250  4
         Object o = get(index);
 251  4
         if (o instanceof JSONArray)
 252  
         {
 253  2
             return (JSONArray) o;
 254  
         }
 255  
 
 256  2
         throw new RuntimeException("JSONArray[" + index + "] is not a JSONArray.");
 257  
     }
 258  
 
 259  
     /**
 260  
      * Get the JSONObject associated with an index.
 261  
      *
 262  
      * @param index subscript
 263  
      * @return A JSONObject value.
 264  
      * @throws RuntimeException If there is no value for the index or if the value is not a JSONObject
 265  
      */
 266  
     public JSONObject getJSONObject(int index)
 267  
     {
 268  4
         Object o = get(index);
 269  4
         if (o instanceof JSONObject)
 270  
         {
 271  2
             return (JSONObject) o;
 272  
         }
 273  
 
 274  2
         throw new RuntimeException("JSONArray[" + index + "] is not a JSONObject.");
 275  
     }
 276  
 
 277  
     /**
 278  
      * Get the long value associated with an index.
 279  
      *
 280  
      * @param index The index must be between 0 and length() - 1.
 281  
      * @return The value.
 282  
      * @throws IllegalArgumentException If the key is not found or if the value cannot be converted to a number.
 283  
      */
 284  
     public long getLong(int index)
 285  
     {
 286  4
         Object o = get(index);
 287  4
         return o instanceof Number ? ((Number) o).longValue() : (long) getDouble(index);
 288  
     }
 289  
 
 290  
     /**
 291  
      * Get the string associated with an index.
 292  
      *
 293  
      * @param index The index must be between 0 and length() - 1.
 294  
      * @return A string value.
 295  
      * @throws RuntimeException If there is no value for the index.
 296  
      */
 297  
     public String getString(int index)
 298  
     {
 299  62
         return get(index).toString();
 300  
     }
 301  
 
 302  
     /**
 303  
      * Determine if the value is null.
 304  
      *
 305  
      * @param index The index must be between 0 and length() - 1.
 306  
      * @return true if the value at the index is null, or if there is no value.
 307  
      */
 308  
     public boolean isNull(int index)
 309  
     {
 310  4
         return get(index) == JSONObject.NULL;
 311  
     }
 312  
 
 313  
     /**
 314  
      * Make a string from the contents of this JSONArray. The <code>separator</code> string is inserted between each
 315  
      * element. Warning: This method assumes that the data structure is acyclical.
 316  
      *
 317  
      * @param separator A string that will be inserted between the elements.
 318  
      * @return a string.
 319  
      * @throws RuntimeException If the array contains an invalid number.
 320  
      */
 321  
     public String join(String separator)
 322  
     {
 323  2078
         int len = length();
 324  2078
         StringBuilder buffer = new StringBuilder();
 325  
 
 326  7172
         for (int i = 0; i < len; i += 1)
 327  
         {
 328  5094
             if (i > 0) buffer.append(separator);
 329  
 
 330  5094
             buffer.append(JSONObject.valueToString(list.get(i)));
 331  
         }
 332  
 
 333  2078
         return buffer.toString();
 334  
     }
 335  
 
 336  
     /**
 337  
      * Get the number of elements in the JSONArray, included nulls.
 338  
      *
 339  
      * @return The length (or size).
 340  
      */
 341  
     public int length()
 342  
     {
 343  2210
         return list.size();
 344  
     }
 345  
 
 346  
     /**
 347  
      * Append an object value. This increases the array's length by one.
 348  
      *
 349  
      * @param value An object value. The value should be a Boolean, Double, Integer, JSONArray, JSONObject, JSONLiteral,
 350  
      *              Long, or String, or the JSONObject.NULL singleton.
 351  
      * @return
 352  
      */
 353  
     public JSONArray put(Object value)
 354  
     {
 355  5168
         assert value != null;
 356  
 
 357  5168
         JSONObject.testValidity(value);
 358  
 
 359  5168
         list.add(value);
 360  
 
 361  5168
         return this;
 362  
     }
 363  
 
 364  
     /**
 365  
      * Put or replace an object value in the JSONArray. If the index is greater than the length of the JSONArray, then
 366  
      * null elements will be added as necessary to pad it out.
 367  
      *
 368  
      * @param index The subscript.
 369  
      * @param value The value to put into the array. The value should be a Boolean, Double, Integer, JSONArray,
 370  
      *              JSONObject, JSONString, Long, or String, or the JSONObject.NULL singeton.
 371  
      * @return
 372  
      * @throws RuntimeException If the index is negative or if the the value is an invalid number.
 373  
      */
 374  
     public JSONArray put(int index, Object value)
 375  
     {
 376  8
         assert value != null;
 377  
 
 378  8
         if (index < 0)
 379  
         {
 380  2
             throw new RuntimeException("JSONArray[" + index + "] not found.");
 381  
         }
 382  
 
 383  6
         JSONObject.testValidity(value);
 384  
 
 385  6
         if (index < length())
 386  
         {
 387  2
             list.set(index, value);
 388  
         }
 389  
         else
 390  
         {
 391  18
             while (index != length()) list.add(JSONObject.NULL);
 392  
 
 393  4
             list.add(value);
 394  
         }
 395  
 
 396  6
         return this;
 397  
     }
 398  
 
 399  
     /**
 400  
      * Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it is not possible to
 401  
      * produce a syntactically correct JSON text then null will be returned instead. This could occur if the array
 402  
      * contains an invalid number.
 403  
      * <p/>
 404  
      * Warning: This method assumes that the data structure is acyclical.
 405  
      *
 406  
      * @return a printable, displayable, transmittable representation of the array.
 407  
      */
 408  
     @Override
 409  
     public String toString()
 410  
     {
 411  
         try
 412  
         {
 413  2078
             return '[' + join(",") + ']';
 414  
         }
 415  0
         catch (Exception e)
 416  
         {
 417  0
             return null;
 418  
         }
 419  
     }
 420  
 
 421  
     Object[] toArray()
 422  
     {
 423  2
         return list.toArray();
 424  
     }
 425  
 
 426  
     @Override
 427  
     public boolean equals(Object obj)
 428  
     {
 429  2030
         if (obj == null) return false;
 430  
 
 431  14
         if (!(obj instanceof JSONArray)) return false;
 432  
 
 433  12
         JSONArray other = (JSONArray) obj;
 434  
 
 435  12
         return list.equals(other.list);
 436  
     }
 437  
 }