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

org.gatein.common.text.FastURLDecoder Maven / Gradle / Ivy

The newest version!
/******************************************************************************
 * JBoss, a division of Red Hat                                               *
 * Copyright 2009, Red Hat Middleware, LLC, and individual                    *
 * contributors as indicated by the @authors tag. See the                     *
 * copyright.txt in the distribution for a full listing of                    *
 * individual contributors.                                                   *
 *                                                                            *
 * This 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 software 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 software; if not, write to the Free                *
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA         *
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.                   *
 ******************************************************************************/
package org.gatein.common.text;

/**
 * @author Julien Viet
 * @version $Revision: 1.1 $
 */
public class FastURLDecoder extends AbstractCharEncoder
{

   /** . */
   private static final FastURLDecoder UTF8 = new FastURLDecoder(CharToByteEncoder.Generic.UTF8);

   /** . */
   private static final FastURLDecoder UTF8_STRICT = new FastURLDecoder(CharToByteEncoder.Generic.UTF8, true);

   public static FastURLDecoder getUTF8Instance()
   {
      return UTF8;
   }

   public static FastURLDecoder getUTF8StrictInstance()
   {
      return UTF8_STRICT;
   }

   /** . */
   private static final char FROM = 0;

   /** . */
   private static final char TO = (char)0x10FFFD;

   /** . */
   private static final int AS_IS = 0;

   /** . */
   private static final int PLUS = 1;

   /** . */
   private static final int ESCAPE = 2;

   /** . */
   private static final int ERROR = 3;

   /** . */
   private final LookupNonTerm root;

   /** . */
   private final int[] decisions;

   /** . */
   private final int[][] conversions;

   /** . */
   private final boolean strict;

   public FastURLDecoder(CharToByteEncoder encoder)
   {
      this(encoder, false);
   }

   public FastURLDecoder(CharToByteEncoder encoder, boolean strict)
   {
      this.strict = strict;

      //
      root = new LookupNonTerm();
      for (char c = FROM; c <= TO; c++)
      {
         byte[] v = encoder.encode(c);
         LookupNonTerm node = root;
         int k = v.length;
         for (int i = 0; i < k; i++)
         {
            int index = (int)v[i] & 0xFF;
            if (i == k - 1)
            {
               node.children[index] = new LookupTerm(c);
            }
            else
            {
               if (node.children[index] == null)
               {
                  node.children[index] = new LookupNonTerm();
               }
               node = (LookupNonTerm)node.children[index];
            }
         }
      }

      //
      conversions = new int[256][256];
      for (char i = 0; i < 256; i++)
      {
         int x = hexValue(i);
         for (char j = 0; j < 256; j++)
         {
            int y = hexValue(j);
            if (x != -1 && y != -1)
            {
               conversions[i][j] = (x << 4) + y;
            }
            else
            {
               conversions[i][j] = -1;
            }
         }
      }

      //
      decisions = new int[256];
      for (int i = 0; i < decisions.length; i++)
      {
         if (TextTools.isAlphaNumeric((char)i))
         {
            decisions[i] = AS_IS;
         }
         else
         {
            switch (i)
            {
               case '+':
                  decisions[i] = PLUS;
                  break;
               case '.':
               case '-':
               case '*':
               case '_':
                  decisions[i] = AS_IS;
                  break;
               case '%':
                  decisions[i] = ESCAPE;
                  break;
               default:
                  decisions[i] = ERROR;
                  break;
            }
         }
      }
   }


   protected void safeEncode(char[] chars, int i, int length, CharWriter tmp)
   {
      while (i < length)
      {
         char c = chars[i++];
         int decision = c < 256 ? decisions[c] : ERROR;
         switch (decision)
         {
            case AS_IS:
               tmp.append(c);
               break;
            case PLUS:
               tmp.append(' ');
               break;
            case ESCAPE:
               int j = i;

               //
               LookupNonTerm current = root;
               while (true)
               {
                  if (j + 2 > length)
                  {
                     throw new MalformedInputException("Not enough chars to decode an escaped value length should have been" +
                        (j + 2) + " but is " + length);
                  }

                  //
                  char c1 = chars[j++];
                  char c2 = chars[j++];

                  //
                  if (c1 > 255)
                  {
                     throw new MalformedInputException("Input out of the lookup range (" + c1 + "," + c2 + ")");
                  }
                  if (c2 > 255)
                  {
                     throw new MalformedInputException("Input out of the lookup range (" + c1 + "," + c2 + ")");
                  }


                  int z = conversions[c1][c2];

                  //
                  if (z == -1)
                  {
                     throw new MalformedInputException("Input out of the lookup range (" + c1 + "," + c2 + ")");
                  }

                  //
                  if (current == null)
                  {
                     throw new MalformedInputException("Cannot decode char '" + c1 + c2 + "'");
                  }
                  else
                  {
                     LookupNode child = current.children[z];
                     if (child instanceof LookupTerm)
                     {
                        LookupTerm term = (LookupTerm)child;
                        tmp.append(term.c);
                        i = j;
                        break;
                     }
                     else
                     {
                        current = (LookupNonTerm)child;
                     }
                  }

                  //
                  j++;
               }
               break;
            case ERROR:
               if (strict)
               {
                  throw new MalformedInputException("Cannot decode char '" + c + "'");
               }
               else
               {
                  tmp.append(c);
               }
               break;
         }
      }
   }

   public boolean isStrict()
   {
      return strict;
   }

   private static class LookupNode
   {
   }

   private static class LookupTerm extends LookupNode
   {

      /** . */
      private final char c;

      public LookupTerm(char c)
      {
         this.c = c;
      }
   }

   private static class LookupNonTerm extends LookupNode
   {
      private final LookupNode[] children = new LookupNode[256];
   }

   /**
    * Returns the hex value of the char c. If the char cannot be converted then -1 is returned.
    *
    * @param c the char to convert
    * @return the converted hex value
    */
   private static int hexValue(char c)
   {
      if (c >= 'A' && c <= 'F')
      {
         return c - 'A' + 10;
      }
      if (c >= 'a' && c <= 'f')
      {
         return c - 'a' + 10;
      }
      if (c >= '0' && c <= '9')
      {
         return c - '0';
      }
      return -1;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy