All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.javascript.JsArray Maven / Gradle / Ivy

There is a newer version: 20241122
Show newest version
package com.hfg.javascript;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.hfg.util.StringUtil;
import com.hfg.xml.XMLException;

//------------------------------------------------------------------------------
/**
 A Javascript array container useful for creating JSON.
 

 @author J. Alex Taylor, hairyfatguy.com
 */
//------------------------------------------------------------------------------
// com.hfg Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------

public class JsArray extends ArrayList implements JsCollection
{
   private static final Pattern UNQUOTED_VALUE_PATTERN = Pattern.compile("(.+?)\\s*[,}\\]]");

   //--------------------------------------------------------------------------
   public JsArray()
   {
      super();
   }

   //--------------------------------------------------------------------------
   public JsArray(int inInitialCapacity)
   {
      super(inInitialCapacity);
   }

   //--------------------------------------------------------------------------
   public JsArray(Collection inCollection)
   {
      super(inCollection);
   }

   //--------------------------------------------------------------------------
   public JsArray(Object[] inArray)
   {
      this();
      if (inArray != null)
      {
         for (Object obj : inArray)
         {
            add(obj);
         }
      }
   }

   //--------------------------------------------------------------------------
   public JsArray(int[] inArray)
   {
      this();
      if (inArray != null)
      {
         for (Object obj : inArray)
         {
            add(obj);
         }
      }
   }

   //--------------------------------------------------------------------------
   public JsArray(long[] inArray)
   {
      this();
      if (inArray != null)
      {
         for (Object obj : inArray)
         {
            add(obj);
         }
      }
   }

   //--------------------------------------------------------------------------
   public JsArray(float[] inArray)
   {
      this();
      if (inArray != null)
      {
         for (Object obj : inArray)
         {
            add(obj);
         }
      }
   }

   //--------------------------------------------------------------------------
   public JsArray(double[] inArray)
   {
      this();
      if (inArray != null)
      {
         for (Object obj : inArray)
         {
            add(obj);
         }
      }
   }

   //--------------------------------------------------------------------------
   public JsArray(CharSequence inJSONString)
   {
      if (StringUtil.isSet(inJSONString))
      {
         parse(inJSONString);
      }
   }

   //###########################################################################
   // PUBLIC METHODS
   //###########################################################################

   //--------------------------------------------------------------------------
   @Override
   public String toString()
   {
      return toJSON();
   }

   //--------------------------------------------------------------------------
   public String toJSON()
   {
      ByteArrayOutputStream outStream;
      try
      {
         outStream = new ByteArrayOutputStream(2048);
         toJSON(outStream);
         outStream.close();
      }
      catch (Exception e)
      {
         throw new XMLException(e);
      }

      return outStream.toString();
   }

   //---------------------------------------------------------------------------
   public void toJSON(OutputStream inStream)
   {
      PrintWriter writer = new PrintWriter(inStream);

      toJSON(writer);

      writer.flush();
   }

   //--------------------------------------------------------------------------
   public void toJSON(Writer inWriter)
   {
      try
      {
         inWriter.write("[ ");

         int i = 0;
         for (Object value : this)
         {
            if (i > 0)
            {
               inWriter.write(", ");
            }

            inWriter.write(composeValueJSON(value));

            i++;
         }

         inWriter.write(" ]");
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
   }

   //--------------------------------------------------------------------------
   public String toJavascript()
   {
      ByteArrayOutputStream outStream;
      try
      {
         outStream = new ByteArrayOutputStream(2048);
         toJavascript(outStream);
         outStream.close();
      }
      catch (Exception e)
      {
         throw new XMLException(e);
      }

      return outStream.toString();
   }

   //---------------------------------------------------------------------------
   public void toJavascript(OutputStream inStream)
   {
      PrintWriter writer = new PrintWriter(inStream);

      toJavascript(writer);

      writer.flush();
   }

   //--------------------------------------------------------------------------
   public void toJavascript(Writer inWriter)
   {
      try
      {
         inWriter.write("[ ");

         int i = 0;
         for (Object value : this)
         {
            if (i > 0)
            {
               inWriter.write(", ");
            }

            inWriter.write(composeValueJavascript(value));

            i++;
         }

         inWriter.write(" ]");
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
   }

   //--------------------------------------------------------------------------
   public String getString(int inIndex)
   {
      Object value = get(inIndex);
      return (value != null ? value.toString() : null);
   }

   //###########################################################################
   // PRIVATE METHODS
   //###########################################################################

   //--------------------------------------------------------------------------
   private String composeValueJSON(Object inValue)
   {
      String value = "null";

      if (inValue != null)
      {
         if (inValue instanceof Number
             || inValue instanceof Boolean)
         {
            value = inValue.toString();
         }
         else if (inValue instanceof JsArray)
         {
            value = ((JsArray) inValue).toJSON();
         }
         else if (inValue instanceof JsObjMap)
         {
            value = ((JsObjMap) inValue).toJSON();
         }
         else
         {
            value = "\"" + JSONUtil.escapeString(inValue.toString()) + "\"";
         }
      }

      return value;
   }

   //--------------------------------------------------------------------------
   private String composeValueJavascript(Object inValue)
   {
      String value = "null";

      if (inValue != null)
      {
         if (inValue instanceof Number
             || inValue instanceof Boolean)
         {
            value = inValue.toString();
         }
         else if (inValue instanceof JsArray)
         {
            value = ((JsArray) inValue).toJavascript();
         }
         else if (inValue instanceof JsObjMap)
         {
            value = ((JsObjMap) inValue).toJavascript();
         }
         else
         {
            value = "\"" + JSONUtil.escapeString(inValue.toString()) + "\"";
         }
      }

      return value;
   }

   //--------------------------------------------------------------------------
   private void parse(CharSequence inString)
   {
      String str = inString.toString().trim();
      if (str.charAt(0) != '['
          || str.charAt(str.length() - 1) != ']')
      {
         throw new RuntimeException("JSON array string must be enclosed with []!");
      }

      int index = 1;

      while (index < str.length() - 1)
      {
         char theChar = str.charAt(index);
         if (Character.isWhitespace(theChar)
             || theChar == ',')
         {
            index++;
         }
         else if (theChar == '[')
         {
            int depth = 1;
            char quote = ' ';
            boolean inEscape = false;
            boolean inValue = false;
            int end = index + 1;
            while (end < str.length() -1
                   && (depth != 1
                       || inValue
                       || str.charAt(end) != ']'))
            {
               char theEndChar = str.charAt(end);
               if (theEndChar == '\\'
                   && inValue)
               {
                  inEscape = ! inEscape;
               }
               else if (inEscape)
               {
                  inEscape = false;
               }

               if (inEscape)
               {
                  continue;
               }

               if (theEndChar == '\''

                        && (str.charAt(end -1) != '\\'
                            || ! inEscape))
               {
                  if (! inValue)
                  {
                     inValue = true;
                     quote = '\'';
                  }
                  else if (quote == '\'')
                  {
                     inValue = false;
                  }
               }
               else if (theEndChar == '"'
                        && (str.charAt(end -1) != '\\'
                            || ! inEscape))
               {
                  if (! inValue)
                  {
                     inValue = true;
                     quote = '"';
                  }
                  else if (quote == '"')
                  {
                     inValue = false;
                  }
               }
               else if (! inValue
                        && theEndChar == '[')
               {
                  depth++;
               }
               else if (! inValue
                        && theEndChar == ']')
               {
                  depth--;
               }

               end++;
            }

            JsArray value = new JsArray(str.substring(index, end + 1));
            add(value);
            index = end + 1;
         }
         else if (theChar == '{')
         {
            // Find the ending brace
            int depth = 1;
            char quote = ' ';
            boolean inValue = false;
            int escapeIdx = -10;
            int end = index + 1;
            while (end < str.length() -1
                   && (depth != 1
                       || inValue
                       || str.charAt(end) != '}'))
            {
               char theEndChar = str.charAt(end);
               if (theEndChar == '\\'
                   && inValue
                   && escapeIdx < end - 1)
               {
                  escapeIdx = end;
               }

               if (theEndChar == '\''
                        && (str.charAt(end -1) != '\\'
                            || escapeIdx < end - 1))
               {
                  if (! inValue)
                  {
                     inValue = true;
                     quote = '\'';
                  }
                  else if (quote == '\'')
                  {
                     inValue = false;
                  }
               }
               else if (theEndChar == '"'
                        && (str.charAt(end -1) != '\\'
                            || escapeIdx < end - 1))
               {
                  if (! inValue)
                  {
                     inValue = true;
                     quote = '"';
                  }
                  else if (quote == '"')
                  {
                     inValue = false;
                  }
               }
               else if (! inValue
                        && theEndChar == '{')
               {
                  depth++;
               }
               else if (! inValue
                        && theEndChar == '}')
               {
                  depth--;
               }

               end++;
            }

//            JsObjMap value = new JsObjMap(StringUtil.replaceAll(str.substring(index, end + 1), "\\", "\\\\"));
            JsObjMap value = new JsObjMap(str.substring(index, end + 1));
            add(value);
            index = end + 1;
         }
         else
         {
            if (theChar == '"')
            {
               boolean inEscape = false;
               int valueStartIndex = index;
               StringBuilder valueBuffer = new StringBuilder();
               // Continue until we find the next unescaped quote.
               while ((index++) < str.length() - 1
                      && ((theChar = str.charAt(index)) != '"')  || inEscape)
               {
                  if (theChar == '\\')
                  {
                     inEscape = ! inEscape;
                  }
                  else if (inEscape)
                  {
                     inEscape = false;
                  }

                  if (! inEscape)
                  {
                     valueBuffer.append(theChar);
                  }
               }

               if (index == str.length() - 1)
               {
                  throw new RuntimeException("Problem parsing value @ position " + valueStartIndex + " in JSON string!");
               }

               index++; // consume the trailing quote
               add(JSONUtil.unescapeString(valueBuffer.toString()));
            }
            else
            {
               Matcher m = UNQUOTED_VALUE_PATTERN.matcher(str);
               if (m.find(index))
               {
                  String stringValue = JSONUtil.unescapeString(m.group(1));
                  index = m.end() - 1;

                  add(JSONUtil.convertStringValueToObject(stringValue));
               }
            }
         }
      }
   }

}