content: /*
   * Copyright (C) 2008 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.common;
  
  import android.text.TextUtils;
  import android.text.util.Rfc822Token;
  import android.text.util.Rfc822Tokenizer;
  import android.util.Patterns;
  import android.widget.AutoCompleteTextView;
  
  import java.util.regex.Pattern;
  
  /**
   * This class works as a Validator for AutoCompleteTextView for
   * email addresses.  If a token does not appear to be a valid address,
   * it is trimmed of characters that cannot legitimately appear in one
   * and has the specified domain name added.  It is meant for use with
   * {@link Rfc822Token} and {@link Rfc822Tokenizer}.
   *
   * @deprecated In the future make sure we don't quietly alter the user's
   *             text in ways they did not intend.  Meanwhile, hide this
   *             class from the public API because it does not even have
   *             a full understanding of the syntax it claims to correct.
   * @hide
   */
  @Deprecated
  public class Rfc822Validator implements AutoCompleteTextView.Validator {
      /**
       * Expression that matches the local part of an email address.
       * This expression does not follow the constraints of the RFC towards the dots, because the
       * de facto standard is to allow them anywhere.
       *
       * It is however a simplification and it will not validate the double-quote syntax.
       */
      private static final String EMAIL_ADDRESS_LOCALPART_REGEXP =
          "((?!\\s)[\\.\\w!#$%&'*+\\-/=?^`{|}~\u0080-\uFFFE])+";
  
      /**
       * Alias of characters that can be used in IRI, as per RFC 3987.
       */
      private static final String GOOD_IRI_CHAR = Patterns.GOOD_IRI_CHAR;
  
      /**
       * Regular expression for a domain label, as per RFC 3490.
       * Its total length must not exceed 63 octets, according to RFC 5890.
       */
      private static final String LABEL_REGEXP =
          "([" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61})?[" + GOOD_IRI_CHAR + "]";
  
      /**
       * Expression that matches a domain name, including international domain names in Punycode or
       * Unicode.
       */
      private static final String DOMAIN_REGEXP =
          "("+ LABEL_REGEXP + "\\.)+"                 // Subdomains and domain
          // Top-level domain must be at least 2 chars
          + "[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]";
  
      /**
       * Pattern for an email address.
       *
       * It is similar to {@link android.util.Patterns#EMAIL_ADDRESS}, but also accepts Unicode
       * characters.
       */
      private static final Pattern EMAIL_ADDRESS_PATTERN =
              Pattern.compile(EMAIL_ADDRESS_LOCALPART_REGEXP + "@" + DOMAIN_REGEXP);
  
      private String mDomain;
      private boolean mRemoveInvalid = false;
  
      /**
       * Constructs a new validator that uses the specified domain name as
       * the default when none is specified.
       */
      public Rfc822Validator(String domain) {
          mDomain = domain;
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean isValid(CharSequence text) {
          Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
          return tokens.length == 1 &&
                 EMAIL_ADDRESS_PATTERN.
                     matcher(tokens[0].getAddress()).matches();
      }
  
      /**
       * Specify if the validator should remove invalid tokens instead of trying
       * to fix them. This can be used to strip results of incorrectly formatted
       * tokens.
       *
       * @param remove true to remove tokens with the wrong format, false to
       *            attempt to fix them
       */
      public void setRemoveInvalid(boolean remove) {
          mRemoveInvalid = remove;
      }
  
      /**
       * @return a string in which all the characters that are illegal for the username
       * or the domain name part of the email address have been removed.
       */
      private String removeIllegalCharacters(String s) {
          StringBuilder result = new StringBuilder();
          int length = s.length();
          for (int i = 0; i < length; i++) {
              char c = s.charAt(i);
  
              /*
               * An RFC822 atom can contain any ASCII printing character
               * except for periods and any of the following punctuation.
               * A local-part can contain multiple atoms, concatenated by
               * periods, so do allow periods here.
               */
  
              if (c <= ' ' || c > '~') {
                  continue;
              }
  
              if (c == '(' || c == ')' || c == '<' || c == '>' ||
                  c == '@' || c == ',' || c == ';' || c == ':' ||
                  c == '\\' || c == '"' || c == '[' || c == ']') {
                  continue;
              }
  
              result.append(c);
          }
          return result.toString();
      }
  
      /**
       * {@inheritDoc}
       */
      public CharSequence fixText(CharSequence cs) {
          // Return an empty string if the email address only contains spaces, \n or \t
          if (TextUtils.getTrimmedLength(cs) == 0) return "";
  
          Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
          StringBuilder sb = new StringBuilder();
  
          for (int i = 0; i < tokens.length; i++) {
              String text = tokens[i].getAddress();
  
              if (mRemoveInvalid && !isValid(text)) {
                  continue;
              }
              int index = text.indexOf('@');
              if (index < 0) {
                  // append the domain of the account if it exists
                  if (mDomain != null) {
                      tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
                  }
              } else {
                  // Otherwise, remove the illegal characters on both sides of the '@'
                  String fix = removeIllegalCharacters(text.substring(0, index));
                  if (TextUtils.isEmpty(fix)) {
                      // if the address is empty after removing invalid chars
                      // don't use it
                      continue;
                  }
                  String domain = removeIllegalCharacters(text.substring(index + 1));
                  boolean emptyDomain = domain.length() == 0;
                  if (!emptyDomain || mDomain != null) {
                      tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain));
                  }
              }
  
              sb.append(tokens[i].toString());
              if (i + 1 < tokens.length) {
                  sb.append(", ");
              }
          }
  
          return sb;
      }
  }
  