Tuesday, December 8, 2009

Common Java Validation using TextUtils Class

TextUtils class used for common Java Regular Expression Check and Validation pupose

package com.joshi.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;



/**
 * Provides convenience methods for common text manipulation and formatting
 *   operations.
 *
 */
public class TextUtils
{
 public static final String REGEX_ALPHANUMERIC;
 public static final String REGEX_NUMERIC;
 public static final String REGEX_SPACE_ALPHANUMERIC;
 public static final String REGEX_US_PHONE;
 public static final String REGEX_EMAIL;
 public static final String REGEX_EMAIL2;
 public static final String DATE_REGEXP;
 public static final String DATE_REGEXP1;
 public static final String DATE_DOB;
 public static final String DATE_REGEXP_SLASHDATE; // MM/DD/YY
        public static final String DATE_REGEXP_FULLSLASHDATE; // MM/DD/YYYY
 public static final String DATE_CCYY_REGEXP;
 public static final String MM_DD;
 public static final String MM_DD2;
 
 
 // OTHERS VALIDATOR REGEX.

 public static final String REGEX_ONLYALPHA;
 public static final String REGEX_SPACE_ONLYALPHA;
 public static final String REGEX_ZIPCODE;
 public static final String REGEX_USZIPCODE;
 public static final String REGEX_CURRENCY;
 
 
 //////////////////////////////////////////////
 
 
 static {
  REGEX_ALPHANUMERIC = "/^[A-Za-z0-9]+$/";
  REGEX_NUMERIC = "/^[0-9]+$/";
  REGEX_SPACE_ALPHANUMERIC = "/^[A-Za-z0-9\\s]+$/";
  REGEX_US_PHONE = "/^((\\(\\d{3}\\) ?)|(\\d{3}-))?\\d{3}-\\d{4}$/";
  REGEX_EMAIL = "/^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$/";
  
  REGEX_EMAIL2 = "\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*";
  
  DATE_REGEXP =
   "m!^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)\\d\\d$!";
  DATE_REGEXP1 =
   "m!^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.]\\d\\d$!";
  DATE_DOB =
   "m!^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](18|19|20|21)\\d\\d$!";
  DATE_REGEXP_SLASHDATE =
     "m!^(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])/\\d\\d$!";
                
                DATE_REGEXP_FULLSLASHDATE =
     "/^(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])/\\d\\d\\d\\d$/";
                
  DATE_CCYY_REGEXP = "m!^(19|20)\\d\\d$!";
  //MM_DD = "/^[0-9]+$/";
  MM_DD = "m!^(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])$!";
  //"/^\\d{2}/\\d{2}$/";  // "/\\d{2}-\\d{2}/";
  MM_DD2 = "m!^(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$!";
  
  //   OTHER ADDED REGEX...
   
  
  REGEX_ONLYALPHA = "^[a-zA-Z]+(([\\'\\,\\.\\-][a-zA-Z])?[a-zA-Z]*)*$";
  
  REGEX_SPACE_ONLYALPHA = "^[a-zA-Z]+((\\s|\\-)[a-zA-Z]+)?$";
  
  REGEX_ZIPCODE = "(^(?!0{5})(\\d{6})?$)";
  
  REGEX_USZIPCODE = "(^(?!0{5})(\\d{5})(?!-?0{4})(-?\\d{4})?$)";
  
  REGEX_CURRENCY = "/^(\\d{1,3}?(\\d{3}?)*\\d{3}(\\.\\d{1,3})?|\\d{1,3}(\\.\\d{1,2})?)$/";
  
  
  //---------------------
  
  
  
 }

 public static String replaceTokens(String p_input, String p_seperator, String p_replaceWith)
 {
  StringBuffer buffer = new StringBuffer();
  if ((p_input != null) && (p_input.length() > 0) && (p_replaceWith != null))
  {
   StringTokenizer stringTokenizer = new StringTokenizer(p_input, p_seperator);
   int nTokens = stringTokenizer.countTokens();
   for (int i=0; i < nTokens; i++)
   {
    if (i > 0)
     buffer.append(p_seperator);
    buffer.append(p_replaceWith);
   }
  }
  return buffer.toString();
 }
 
 public static String replaceAll(
  String p_input,
  char p_toReplace,
  String p_replaceWith)
 {
  if ((p_input == null)
   || (p_input.length() == 0)
   || (p_replaceWith == null))
  {
   return p_input;
  }

  int fromIndex = 0;
  int index = -1;
  StringBuffer buffer = new StringBuffer();

  while ((index = p_input.indexOf(p_toReplace, fromIndex)) != -1)
  {
   buffer.append(p_input.substring(fromIndex, index) + p_replaceWith);
   fromIndex = index + 1;
  }

  if (fromIndex < p_input.length())
  {
   buffer.append(p_input.substring(fromIndex));
  }

  return buffer.toString();
 }

 public static String replaceAll(
  String p_input,
  String p_toReplace,
  String p_replaceWith)
 {
  if ((p_input == null)
   || (p_input.length() == 0)
   || (p_toReplace == null)
   || (p_replaceWith == null))
  {
   return p_input;
  }

  int fromIndex = 0;
  int index = -1;
  StringBuffer buffer = new StringBuffer();
  int toReplaceLength = p_toReplace.length();

  while ((index = p_input.indexOf(p_toReplace, fromIndex)) != -1)
  {
   buffer.append(p_input.substring(fromIndex, index) + p_replaceWith);
   fromIndex = index + toReplaceLength;
  }

  if (fromIndex < p_input.length())
  {
   buffer.append(p_input.substring(fromIndex));
  }

  return buffer.toString();
 }

 public static String replaceAll(
  String p_input,
  String p_toReplace,
  List p_replaceWith)
 {

  int nReplaceWith = (p_replaceWith != null) ? p_replaceWith.size() : 0;
  if (nReplaceWith == 0)
   return p_input;

  StringBuffer buffer = new StringBuffer(512);
  int fromIndex = 0;
  int index = -1;
  int toReplaceLength = p_toReplace.length();
  String s;
  int i = 0;
  while ((index = p_input.indexOf(p_toReplace, fromIndex)) != -1)
  {
   if (i < nReplaceWith)
   {
    s = (String) p_replaceWith.get(i);
    i++;
   }
   else
    s = "";
   buffer.append(p_input.substring(fromIndex, index) + s);
   fromIndex = index + toReplaceLength;
  }
  if (fromIndex < p_input.length())
  {
   buffer.append(p_input.substring(fromIndex));
  }
  return buffer.toString();
 }

 public static String truncate(String p_input, int p_maxLength)
 {
  if (p_input == null || p_input.trim().length() == 0)
  {
   return p_input;
  }

  p_input = p_input.trim();
  int length = p_input.length();

  if (length <= p_maxLength)
  {
   return p_input;
  }

  p_input = p_input.substring(0, p_maxLength);
  int index = p_input.lastIndexOf(" ");

  if (index != -1)
  {
   p_input = p_input.substring(0, index);
  }

  return p_input;
 }

 public static boolean isAlphanumeric(String p_value)
 {
  return p_value.matches(REGEX_ALPHANUMERIC);
 }
 
 /**
  * Check if the string is alpha and numeric
  * @param p_value
  * @return
  */
 public static boolean isAlphaAndNumeric(String p_value)
 {
     if (isAlphanumeric(p_value))
     {
         if (p_value.matches("(.*)[0-9]+(.*)") && p_value.matches("(.*)[a-z]+(.*)"))
         {
             return true;
         }
         if (p_value.matches("(.*)[0-9]+(.*)") && p_value.matches("(.*)[A-Z]+(.*)"))
         {
             return true;
         }
     }
     return false;
 }

 public static boolean isNumeric(String p_value)
 {
  return p_value.matches(REGEX_NUMERIC);
 }
 
        public static boolean isDigit(String p_value) {
            try {
                Double d = Double.parseDouble(p_value);
            }
            catch(Exception e) {
                return false;
            }
            return true;
        }
 public static boolean isCurrency(String p_value)
 {
  return p_value.matches(REGEX_CURRENCY);
 }

 public static boolean isAlphanumericOrSpace(String p_value)
 {
  return p_value.matches(
   REGEX_SPACE_ALPHANUMERIC);
 }

 public static boolean isMMDDYYYYDate(String p_value)
 {
  return p_value.matches(DATE_REGEXP);
 }

 public static boolean isDOBDate(String p_value)
 {
  return p_value.matches(DATE_DOB);
 }
 
 public static boolean isMMDDDate(String p_value)
 {
  return p_value.matches(MM_DD);
 }
 
 public static boolean isMMDDDate2(String p_value)
 {
  return p_value.matches(MM_DD2);
 }

 public static boolean isInteger(String p_value)
 {
  int i = 0;

  try
  {
   i = Integer.parseInt(p_value);
  }
  catch (NumberFormatException e)
  {
   return false;
  }

  return true;
 }

 public static boolean isPositiveInteger(String p_value)
 {
  int i = 0;

  try
  {
   i = Integer.parseInt(p_value);
  }
  catch (NumberFormatException e)
  {
   return false;
  }

  if (i <= 0)
  {
   return false;
  }

  return true;
 }
 public static boolean isPositiveIntegerGreaterThanEqualToZero(String p_value)
 {
  int i = 0;

  try
  {
   i = Integer.parseInt(p_value);
  }
  catch (NumberFormatException e)
  {
   return false;
  }

  if (i < 0)
  {
   return false;
  }

  return true;
 }

 public static boolean isPositiveLong(String p_value)
 {
  long i = 0;

  try
  {
   i = Long.parseLong(p_value);
  }
  catch (NumberFormatException e)
  {
   return false;
  }

  if (i <= 0)
  {
   return false;
  }

  return true;
 }

 public static boolean isDouble(String p_value)
 {
  double d = 0;

  try
  {
                    
   d = Double.parseDouble(p_value);
  }
  catch (NumberFormatException e)
  {
                    System.out.print("Error in isdouble :: ");
                    e.printStackTrace();
   return false;
  }

  return true;
 }

 public static boolean isFloat(String p_value)
 {
  float f = 0;

  try
  {
   f = Float.parseFloat(p_value);
  }
  catch (NumberFormatException e)
  {
   return false;
  }

  return true;
 }

 public static String generateRandomNumeric(int p_length)
 {
  int num = 0;
  StringBuffer value = new StringBuffer("");

  for (int i = 0; i < p_length; i++)
  {
   num = (new Double(Math.random() * 10)).intValue();
   value.append(num);
  }

  return value.toString();
 }

 public static boolean isUsPhone(String p_value)
 {
  return p_value.matches(REGEX_US_PHONE);
 }

 public static boolean isEmail(String p_value)
 {
  //return m_emailMatcher.match(REGEX_EMAIL, p_value);
  return p_value.matches(REGEX_EMAIL2);
 }

 public static void showTrace(String p_msg)
 {
  try
  {
   throw new Exception(p_msg);
  }
  catch (Exception ex)
  {
   ex.printStackTrace();
  }
 }

 public static String maskText(String p_text, int p_to, char p_char)
 {
  return maskText(p_text, 0, p_to, p_char);
 }
 
 public static String maskText(String p_text, int p_to)
 {
  return maskText(p_text, 0, p_to, 'X');
 }
 
 public static String maskText(String p_text, int p_from, int p_to, char p_char)
 {
  int p_textLen = (p_text != null) ? p_text.length() : 0;
  StringBuffer maskedText = new StringBuffer(p_textLen);
  if (p_from >= 0 && p_from < p_to)
  {
   maskedText.append(p_text.substring(0, p_from));
   for (int i = p_from; i < p_to; i++)
   {
    maskedText.append(p_char);
   }
   maskedText.append(p_text.substring(p_to));
  }
  return maskedText.toString();
 }

 public static String trim(String p_in)
 {
  if (p_in == null)
  {
   return "";
  }
  return p_in.trim();
 }

 public static List splitToList(
  String p_string,
  String p_delim,
  boolean p_caseSensitive)
 {
  List lList = new ArrayList();
  int liPos = 0;
  int liPrevPos = 0;
  String lsString = noNull(p_string);
  String lsDelim = noNull(p_delim);
  int liLen = lsDelim.length();

  if (lsString.equals("") || lsDelim.equals(""))
  {
   lList.add(p_string);
   return lList;
  }

  if (!p_caseSensitive)
  {
   lsString = lsString.toLowerCase();
   lsDelim = lsDelim.toLowerCase();
  }

  ///
  liPrevPos = 0;
  liPos = lsString.indexOf(lsDelim);
  while (liPos >= 0)
  {
   if (liPos == 0)
   {
    lList.add("");
   }
   else
   {
    lList.add(p_string.substring(liPrevPos, liPos));
   }
   liPrevPos = liPos + liLen;
   liPos = lsString.indexOf(lsDelim, liPrevPos);
  }

  if (liPrevPos > 0)
  {
   lList.add(p_string.substring(liPrevPos));
  }

  if (lList.size() == 0)
  {
   lList.add(p_string);
  }

  return lList;
 }

 public static String noNull(String p_string)
 {
  return (((p_string == null)) ? "" : p_string);
 }

 public static String noNull(int p_val)
 {
  return (((p_val == 0)) ? "" : Integer.toString(p_val));
 }

 public static String noNull(long p_val)
 {
  return (
   ((new Long(p_val)).intValue() == 0) ? "" : Long.toString(p_val));
 }

 public static String noNull(Long p_val)
 {
  return (
   ((p_val == null || p_val.intValue() == 0)) ? "" : p_val.toString());
 }

 public static String noNull(double p_val)
 {
  return (
   (new Double(p_val).intValue() == 0) ? "" : Double.toString(p_val));
 }
 public static String noNull(char p_val)
 {
  return (
   (new Character(p_val).charValue() == 0)
    ? ""
    : Character.toString(p_val));
 }

 public static int getTokenCount(String p_value, String p_delim)
 {
  StringTokenizer strK = new StringTokenizer(p_value, p_delim);
  return strK.countTokens();
 }

 public static String removeNumeric(String p_string)
 {
  int size = p_string.length();
  String str;
  StringBuffer strBuff = new StringBuffer();
  for (int i = 1; i <= size; i++)
  {
   str = p_string.substring(i - 1, i);
   if (!isNumeric(str))
   {
    strBuff.append(str);
   }
  }
  return strBuff.toString();
 }

 /**
  * @param m_eventDateFrom
  * @return
  */
 public static boolean isMMDDYYDate(String p_value)
 {

  return p_value.matches(DATE_REGEXP1);
 }

 /**
  * Check the date 
  * @param p_value
  * @return if the pattern is mm/dd/yy return true. otherwise return false
 */
 public static boolean isMMSlashDDSlashYYDate(String p_value)
 {
  return p_value.matches(DATE_REGEXP_SLASHDATE);
 }
        
        /**
  * Check the date 
  * @param p_value
  * @return if the pattern is mm/dd/yyyy return true. otherwise return false
 */
 public static boolean isMMSlashDDSlashYYYYDate(String p_value)
 {
  //return p_value.matches(DATE_REGEXP_FULLSLASHDATE);
                 return Pattern.matches(DATE_REGEXP_FULLSLASHDATE, p_value);
 }
 /**
  * @param p_value
  * @return boolean
  */
 public static boolean isCCYY(String p_value)
 {
  return p_value.matches(DATE_CCYY_REGEXP);
 }

 /**
  * parse parameter string  
  * 
  * @param p_paramString The Parameter string 
  * @param p_paramPairDelim The delimeter of the parameter pair
  * @param p_paramDelim The delimeter of the parameter
  * @return A HashMap of parameters.
  */
 public static HashMap parseParamString(String p_paramString)
 {
  return parseParamString(p_paramString, "&", "=");
 }
 
 /**
  * parse parameter string  
  * 
  * @param p_paramString The Parameter string 
  * @param p_paramPairDelim The delimeter of the parameter pair
  * @param p_paramDelim The delimeter of the parameter
  * @return A HashMap of parameters.
  */
 public static HashMap parseParamString(
  String p_paramString,
  String p_paramPairDelim,
  String p_paramDelim)
 {

  HashMap map = new HashMap();
  if (p_paramString != null
   && p_paramString.length() > 0
   && p_paramPairDelim != null
   && p_paramPairDelim.length() > 0
   && p_paramDelim != null
   && p_paramDelim.length() > 0)
  {

   StringTokenizer tokenizer =
    new StringTokenizer(p_paramString, p_paramPairDelim);
   String token, name, value;
   int p_paramDelimLength = p_paramDelim.length();
   int pos = 0;

   while (tokenizer.hasMoreTokens())
   {
    token = tokenizer.nextToken();
    if (token != null
     && token.length() > 0
     && (pos = token.indexOf(p_paramDelim)) != -1)
    {
     name = token.substring(0, pos);
     value = token.substring(pos + p_paramDelimLength);
     if (name != null
      && value != null
      && (name = name.trim()).length() > 0)
     {
      value = value.trim();
      map.put(name.trim(), value.trim());
     }
    }
   }
  }
  return map;
 }

 public static boolean toBoolean(String p_value, boolean p_defaultValue)
 {
  boolean value = p_defaultValue;
  if (p_value != null && p_value.length() > 0)
  {
   try
   {
    Boolean b = new Boolean(p_value);
    value = b.booleanValue();
   }
   catch (Throwable e)
   {}
  }
  return value;
 }

 public static boolean isNotNullNotEmpty(String p_string)
 {
  return isNotNullNotEmpty(p_string, false);
 }
 
 public static boolean isNotNullNotEmpty(String p_string, boolean p_trim)
 {
  if (p_trim)
  {
   if (p_string != null && p_string.trim().length() > 0)
   {
    return true;
   }
  }
  else
  {
   if (p_string != null && p_string.length() > 0)
   {
    return true;
   }
  }
  return false;
 }
 
 public static boolean isNullAndEmpty(String p_string)
 {
  return isNullAndEmpty(p_string, true);
 }
 
 public static boolean isNullAndEmpty(String p_string, boolean p_trim)
 {
  if (p_trim)
  {
   if (p_string == null || p_string.trim().length() == 0)
   {
    return true;
   }
  }
  else
  {
   if (p_string == null || p_string.length() == 0)
   {
    return true;
   }
  }
  return false;
 }

 /**
  * Return String of all attributes
  * 
  * @param p_map The Map
  * @return String 
  */
 public static String mapToString(Map p_map)
 {
  return mapToString(p_map, false);
 }

 /**
  * Return String of all attributes
  * 
  * @param p_map The Map
  * @param p_detail true if all Object is called using toString() 
  * @return String 
  */
 public static String mapToString(Map p_map, boolean p_detail)
 {
  StringBuffer buffer = new StringBuffer();
  Set set = p_map.keySet();
  Iterator iterator = (set != null) ? set.iterator() : null;
  if (iterator != null)
  {
   String name;
   Object value;
   while (iterator.hasNext())
   {
    name = (String) iterator.next();
    buffer.append("  " + name + "=");
    if ((value = p_map.get(name)) == null)
     buffer.append("null");
    else if (value instanceof String)
     buffer.append(value);
    else if (p_detail)
     buffer.append(value);
    else
     buffer.append(value.getClass().getName());
    buffer.append("\n");
   }
  }
  return buffer.toString();
 }

 

 public static String decodeXML(String p_string)
 {
  String returnString = replaceAll(p_string, "&amp;", "&");
  returnString = replaceAll(returnString, "&apos;", "'");
  returnString = replaceAll(returnString, "&quot;", "\"");
  returnString = replaceAll(returnString, "&lt;", "<");
  returnString = replaceAll(returnString, "&gt;", ">");
  return returnString;
 }
 /**
  * convert MM-DD to MM/DD
  * @param p_inputStr
  * @return
  */
 public static String convertToMMDDPattern(String p_inputStr)
 {
  String returnStr = null;
  if (!isNotNullNotEmpty(p_inputStr))
   returnStr = "";
  else if (isMMDDDate(p_inputStr) && p_inputStr.length() == 5)
   returnStr = p_inputStr;
  else
  {
   StringBuffer sb = new StringBuffer();
   returnStr = replaceAll(p_inputStr, '-', "/");
   String temp[] = returnStr.split("/");
   if (temp.length != 2)
    returnStr = "";
   else
   {
    String single =
     (temp[0].length() == 1 ? "0" + temp[0] : temp[0]);
    sb.append(single);
    sb.append("/");
    single = (temp[1].length() == 1 ? "0" + temp[1] : temp[1]);
    sb.append(single);
    returnStr = sb.toString();
   }
  }

  return returnStr;
 }
 
 /**
  * Method will convert list into String by appending the deliminator
  * @param p_list - List
  * @param p_delim - Deliminator to be used
  * @return - String representation of List by calling toString() method on all objects in list.
  *  e.g return something like 1,2,3,4 etc.
  */
 public static String listToString(List p_list,String p_delim)
 {
  StringBuffer buffer = new StringBuffer();
  Iterator iterator = (p_list != null) ? p_list.iterator() : null;
  if (iterator != null)
  {
   String name;
   boolean first = true;
   while (iterator.hasNext())
   {
    name = (String) iterator.next();
    if(name != null)
    {
     if(first)
     {
      first = false;
     }
     else
     {
      buffer.append(p_delim);
     }
     buffer.append(name);
    }
   }
  }
  return buffer.toString();
 }
 
 /**
  * @param string
  * @param i
  * @return
  */
 public static int toInteger(String p_value, int p_defaultValue) {
  int value = p_defaultValue;
  if (p_value != null && p_value.length() > 0) 
  {
   try 
   {
    value = Integer.parseInt(p_value);
   } catch (Throwable e) {
   }
  }
  return value; 
 }
 
 public static String appendWithSeperator(String p_text1, String p_text2, String p_seperator)
 {
  // Return p_text1 + p_seperator + p_text2 if both p_text1 & p_text2 are not null and empty
  // Othersize return p_text1 or p_text2 depending on which one is not null and empty
  int size1 = (p_text1 == null) ? 0: p_text1.length();
  int size2 = (p_text2 == null) ? 0: p_text2.length();
  if (size1 > 0 && size2 > 0)
   return p_text1.concat(p_seperator).concat(p_text2);
  else if (size1 > 0)
   return p_text1;
  else
   return p_text2;
 }
 

    public static String removeSpecialCharacters(String p_xml)
    {
        p_xml = TextUtils.replaceAll(p_xml,"&amp;apos;","&#38;apos;");
        p_xml = TextUtils.replaceAll(p_xml,"&amp;","&#38;amp;");
  p_xml = TextUtils.replaceAll(p_xml,"&lt;","&#38;lt;");
  p_xml = TextUtils.replaceAll(p_xml,"&gt;","&#38;gt;");
        return p_xml;
    }
    
    public static boolean isOnlyAlpha(String p_value)
 {
  //return m_emailMatcher.match(REGEX_EMAIL, p_value);
     // Format XXXXX
  return p_value.matches(REGEX_ONLYALPHA);
 }  
    
    public static boolean isSpaceWithAlpha(String p_value)
 {
  //return m_emailMatcher.match(REGEX_EMAIL, p_value);
     // Format XXX XXXX
  return p_value.matches(REGEX_SPACE_ONLYALPHA);
 }
    
    public static boolean isUsZipCode(String p_value)
 {
  //return m_emailMatcher.match(REGEX_EMAIL, p_value);
     /// Format 12345-1234
  return p_value.matches(REGEX_USZIPCODE);
 }
    
    public static boolean isZipCode(String p_value)
 {
  //return m_emailMatcher.match(REGEX_EMAIL, p_value);
     // Format 123456
  return p_value.matches(REGEX_ZIPCODE);
 }
}

2 comments:

Unknown said...

Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I’ll be subscribing to your feed and I hope you post again soon. heather grey melange yarn

Anonymous said...


Great Post,really it was very helpful for us.
Thanks a lot for sharing!
I found this blog to be very useful!!
JAVA training in Bangalore

Contributors