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

org.gatein.common.util.ParameterMap Maven / Gradle / Ivy

There is a newer version: 2.2.2.Final
Show 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.util;

import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * A decorator that enforce the map content to be . It also provides capabilities for making a copy of
 * the value either on a read or on a write.
 *
 * @author Julien Viet
 * @version $Revision: 6671 $
 */
@SuppressWarnings("serial")
public final class ParameterMap extends AbstractTypedMap implements Serializable
{

   /** Just a string to string converter. */
   private static final KeyConverter keyConv = new KeyConverter();

   /**
    * Copy the parameter map.
    *
    * @param map the parameter map to copy
    * @return a parameter map initialized from the argument map
    * @throws NullPointerException     if the map contains a null key or a null value
    * @throws IllegalArgumentException if the map is null or it contains a value with a zero length array or a null
    *                                  element in the array
    * @throws ClassCastException       if the map contains a key that is not a string or a value that is not a string
    *                                  array
    */
   public static ParameterMap clone(Map map) throws NullPointerException, ClassCastException, IllegalArgumentException
   {
      return clone(map, AccessMode.A);
   }

   /**
    * Copy the parameter map.
    *
    * @param map        the parameter map to copy
    * @param accessMode the access mode
    * @return a parameter map initialized from the argument map
    * @throws NullPointerException     if the map contains a null key or a null value
    * @throws IllegalArgumentException if the map is null or it contains a value with a zero length array or a null
    *                                  element in the array
    * @throws ClassCastException       if the map contains a key that is not a string or a value that is not a string
    *                                  array
    */
   public static ParameterMap clone(Map map, AccessMode accessMode) throws NullPointerException, ClassCastException, IllegalArgumentException
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(map, "map to be cloned");

      //
      HashMap delegate = new HashMap(map);
      return new ParameterMap(delegate, accessMode);
   }

   /**
    * Safely wrap the map as a portlet parameters object. If the map is already a parameter map object, just return that
    * object otherwise return a wrapper around the map.
    *
    * @param map the map
    * @return the portlet parameters
    */
   public static ParameterMap wrap(Map map)
   {
      return wrap(map, AccessMode.A);
   }

   /**
    * Safely wrap the map as a portlet parameters object. If the map is already a parameter map object, just return that
    * object with the new access mode otherwise return a wrapper around the map.
    *
    * @param map        the map
    * @param accessMode the access mode
    * @return the portlet parameters
    */
   public static ParameterMap wrap(Map map, AccessMode accessMode)
   {
      if (map instanceof ParameterMap)
      {
         return new ParameterMap((ParameterMap)map, accessMode);
      }
      else
      {
         return new ParameterMap(map, accessMode);
      }
   }

   /** . */
   private transient AccessMode accessMode;

   /** . */
   private Map delegate;

   public ParameterMap()
   {
      this(AccessMode.A);
   }

   public ParameterMap(AccessMode accessMode)
   {
      this(new HashMap(), accessMode);
   }

   public ParameterMap(Map delegate)
   {
      this(delegate, AccessMode.A);
   }

   public ParameterMap(Map delegate, AccessMode accessMode)
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(delegate, "delegate");
      ParameterValidation.throwIllegalArgExceptionIfNull(accessMode, "access");

      //
      this.delegate = delegate;
      this.accessMode = accessMode;
   }

   /**
    * Exposed through the as(AccessMode accessMode) method.
    *
    * @param that       the map to rewrap
    * @param accessMode the new access mode
    */
   private ParameterMap(ParameterMap that, AccessMode accessMode)
   {
      this(that != null ? that.delegate : null, accessMode);
   }

   public AccessMode getAccessMode()
   {
      return accessMode;
   }

   public ParameterMap as(AccessMode accessMode)
   {
      return new ParameterMap(this, accessMode);
   }

   public Converter getKeyConverter()
   {
      return keyConv;
   }

   public Converter getValueConverter()
   {
      return accessMode.converter;
   }

   protected Map getDelegate()
   {
      return delegate;
   }

   /**
    * Return the parameter value or null if it does not exist.
    *
    * @param name the parameter name
    * @return the parameter value or null if it does not exist
    * @throws IllegalArgumentException if the name is null
    */
   public String getValue(String name) throws IllegalArgumentException
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(name, "parameter name");

      // Access delegate directly to avoid copy on read when it is enabled
      String[] value = delegate.get(name);

      //
      return value == null ? null : value[0];
   }

   /**
    * Return the parameter values or null if it does not exist.
    *
    * @param name the value to get
    * @return the parameter values
    * @throws IllegalArgumentException if the name is null
    */
   public String[] getValues(String name) throws IllegalArgumentException
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(name, "parameter name");

      //
      return get(name);
   }

   /**
    * Set the a parameter value.
    *
    * @param name  the parameter name
    * @param value the parameter value
    * @throws IllegalArgumentException if the name or the value is null
    */
   public void setValue(String name, String value) throws IllegalArgumentException
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(name, "parameter name");
      ParameterValidation.throwIllegalArgExceptionIfNull(value, "parrameter value");

      // Access delegate directly to avoid copy on read write it is enabled
      delegate.put(name, new String[]{value});
   }

   /**
    * Set the parameter values. This method does not make a defensive copy of the values.
    *
    * @param name   the parameter name
    * @param values the parameter values
    * @throws NullPointerException     if the name or the value is null
    * @throws IllegalArgumentException if the name is null or the values is null or the values length is zero or
    *                                  contains a null element
    */
   public void setValues(String name, String[] values) throws IllegalArgumentException
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(name, "parameter name");
      ParameterValidation.throwIllegalArgExceptionIfNull(values, "parameter values");

      //
      put(name, values);
   }

   /**
    * Append the content of the argument map to that map. If both maps contains an entry sharing the same key, then the
    * string arrays or the two entries will be concatenated into a single array. Each entry present on the argument map
    * and not in the current map will be kept as is. The argument validation is performed before the state is updated.
    *
    * @param params the parameters to appends
    * @throws NullPointerException     if the map contains a null key or a null value
    * @throws IllegalArgumentException if the map is null or it contains a value with a zero length array or a null
    *                                  element in the array
    * @throws ClassCastException       if the map contains a key that is not a string or a value that is not a string
    *                                  array
    */
   public void append(Map params) throws ClassCastException, NullPointerException, IllegalArgumentException
   {
      // Clone to have an atomic operation
      params = new HashMap(params);

      //
      for (Map.Entry entry : params.entrySet())
      {
         String[] appendedValue = entry.getValue();

         //
         String[] existingValue = delegate.get(entry.getKey());
         if (existingValue != null)
         {
            // Perform the concatenation operation if the entry exist
            String[] newValue = new String[existingValue.length + appendedValue.length];
            System.arraycopy(existingValue, 0, newValue, 0, existingValue.length);
            System.arraycopy(appendedValue, 0, newValue, existingValue.length, appendedValue.length);
            appendedValue = newValue;
         }
         else
         {
            // Clone the new value otherwise we would modify an the passed map
            appendedValue = appendedValue.clone();
         }

         //
         entry.setValue(appendedValue);
      }

      //
      putAll(params);
   }

   public String toString()
   {
      StringBuffer buffer = new StringBuffer("ParameterMap[");
      for (Iterator> i = entrySet().iterator(); i.hasNext();)
      {
         Map.Entry entry = i.next();
         String name = (String)entry.getKey();
         String[] values = (String[])entry.getValue();
         buffer.append(name);
         for (int j = 0; j < values.length; j++)
         {
            buffer.append(j > 0 ? ',' : '=').append(values[j]);
         }
         if (i.hasNext())
         {
            buffer.append(" | ");
         }
      }
      buffer.append(']');
      return buffer.toString();
   }

   private void writeObject(java.io.ObjectOutputStream out) throws IOException
   {
      out.defaultWriteObject();
      out.writeBoolean(accessMode.copyValueOnRead);
      out.writeBoolean(accessMode.copyValueOnWrite);
      out.writeObject(delegate);
   }

   @SuppressWarnings("unchecked")
   private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
   {
      in.defaultReadObject();
      boolean copyValueOnRead = in.readBoolean();
      boolean copyValueOnWrite = in.readBoolean();

      //
      accessMode = AccessMode.get(copyValueOnRead, copyValueOnWrite);
      delegate = (Map)in.readObject();
   }

   private static class KeyConverter extends Converter
   {
      protected String getInternal(String external) throws IllegalArgumentException, ClassCastException
      {
         return external;
      }

      protected String getExternal(String internal)
      {
         return internal;
      }

      protected boolean equals(String left, String right)
      {
         return left.equals(right);
      }
   }

   /** Defines how the state of the values of a parameter map are managed. */
   public static class AccessMode
   {

      /**
       * Factory method for an access mode.
       *
       * @param copyValueOnRead  if true the value will be copied on a read
       * @param copyValueOnWrite if true the value will be copied on a write
       * @return the convenient access mode
       */
      public static AccessMode get(boolean copyValueOnRead, boolean copyValueOnWrite)
      {
         return copyValueOnRead ? copyValueOnWrite ? D : C : copyValueOnWrite ? B : A;
      }

      /** . */
      private static final AccessMode A = new AccessMode(false, false);

      /** . */
      private static final AccessMode B = new AccessMode(false, true);

      /** . */
      private static final AccessMode C = new AccessMode(true, false);

      /** . */
      private static final AccessMode D = new AccessMode(true, true);

      /** . */
      private final boolean copyValueOnRead;

      /** . */
      private final boolean copyValueOnWrite;

      /** . */
      private final ValueConverter converter;

      private AccessMode(boolean copyValueOnRead, boolean copyOnWrite)
      {
         this.copyValueOnRead = copyValueOnRead;
         this.copyValueOnWrite = copyOnWrite;
         this.converter = new ValueConverter(this);
      }

      public boolean getCopyValueOnRead()
      {
         return copyValueOnRead;
      }

      public boolean getCopyValueOnWrite()
      {
         return copyValueOnWrite;
      }
   }

   private static class ValueConverter extends Converter
   {

      /** . */
      private final AccessMode accessMode;

      private ValueConverter(AccessMode accessMode)
      {
         this.accessMode = accessMode;
      }

      /**
       * Only check are made to the value. The only valid values accepted are string arrays with non zero length and
       * containing non null values.
       *
       * @param external
       * @return
       * @throws NullPointerException     if the value is null
       * @throws ClassCastException       if the value type is not an array of string
       * @throws IllegalArgumentException if the array length is zero or one of the array value is null
       */
      protected String[] getInternal(String[] external) throws IllegalArgumentException, ClassCastException, NullPointerException
      {
         if (external.length == 0)
         {
            throw new IllegalArgumentException("Array must not be zero length");
         }

         //
         for (int i = external.length - 1; i >= 0; i--)
         {
            if (external[i] == null)
            {
               throw new IllegalArgumentException("No null entries allowed in String[]");
            }
         }

         //
         if (accessMode.copyValueOnWrite)
         {
            external = external.clone();
         }

         //
         return external;
      }

      protected String[] getExternal(String[] internal)
      {
         if (accessMode.copyValueOnRead)
         {
            internal = internal.clone();
         }
         return internal;
      }

      protected boolean equals(String[] left, String[] right)
      {
         return Arrays.equals(left, right);
      }
   }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy