/*
* Copyright (C) 2012 Works Applications Co., Ltd.
* http://www.worksap.co.jp/
*
* Licensed under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
*/
package jp.co.worksap.message.parser;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import com.google.common.base.Strings;
import jp.co.worksap.message.decoder.HeaderDecoder;
import jp.co.worksap.message.util.CharsetUtility;
import jp.co.worksap.message.util.Encoding;
public class AddressParser {
private static final Pattern IN_BRACKET_FILTER = Pattern.compile("<(.*)>",
Pattern.CASE_INSENSITIVE);
private static final Pattern IN_DOUBLE_QUOTATION_FILTER = Pattern.compile(
"\"(.*)\"", Pattern.CASE_INSENSITIVE);
private static final Pattern OUT_OF_BRACKET_FILTER = Pattern.compile(
"(.*)<.*>", Pattern.CASE_INSENSITIVE);
/**
* as possible as decoding the String value
*
* @param garble
* @param constructable
* @return
* @throws MessagingException
*/
public InternetAddress[] fixAddress(Address[] garble,
String[] constructable) throws MessagingException {
// it returns an empty array instead of null when there is no recipient.
if (garble == null || constructable == null) {
return new InternetAddress[0];
}
String[] raw = splitAddressString(constructable);
Address[] address = resplitAddress(garble);
// these addresses must be wrong.
if (raw.length != address.length) {
InternetAddress[] fixed = new InternetAddress[raw.length];
for (int i = 0; i < raw.length; i++) {
fixed[i] = getAddressFromRawString(raw[i]);
}
return fixed;
}
// these addresses may be wrong
InternetAddress[] fixed = new InternetAddress[raw.length];
for (int i = 0; i < raw.length; i++) {
// "undecodable" means Message class can't decode it
String lowerCase = raw[i].toLowerCase();
boolean undecodableUtf8 = lowerCase.contains(Encoding.UTF8)
&& raw[i].contains("\r\n");
if (undecodableUtf8 || CharsetUtility.needsMapping(lowerCase)) {
fixed[i] = getAddressFromRawString(raw[i]);
} else {
fixed[i] = (InternetAddress) address[i];
fixed[i].getPersonal();
}
}
return fixed;
}
public InternetAddress getAddressFromRawString(String text)
throws MessagingException {
HeaderDecoder decoder = new HeaderDecoder();
String decoded = decoder.decodeAddress(text);
String email = getEmail(decoded);
String personal = null;
if (text.contains("\"")) {
personal = getPersonal(decoded);
} else {
personal = getPersonalWithQuote(decoded);
}
if (personal.isEmpty()) {
try {
return new InternetAddress(email);
} catch (AddressException e) {
throw new MessagingException(
"Can not get the address because its format is wrong",
e);
}
} else {
try {
return new InternetAddress(email, personal);
} catch (UnsupportedEncodingException e) {
throw new MessagingException(
"Can not get the address because of an unsupported encoding",
e);
}
}
}
private String[] splitAddressString(String[] address) {
List<String> list = new ArrayList<String>();
for (String ad : address) {
String[] splitted = ad.split(",");
for (int i = 0; i < splitted.length; i++) {
// to repair double-quoted comma
if (isEvenNumberOfQuot(splitted[i])) {
// this is closed quotations
list.add(splitted[i]);
} else {
// this is opened quotations
StringBuilder sb = new StringBuilder();
sb.append(splitted[i]);
i++;
for (/* do nothing */; i < splitted.length; i++) {
sb.append(",");
sb.append(splitted[i]);
if (!isEvenNumberOfQuot(splitted[i])) {
// quotations become closed
break;
}
}
list.add(sb.toString());
}
}
}
return list.toArray(new String[0]);
}
private Address[] resplitAddress(Address[] garble)
throws MessagingException {
List<Address> addressList = new ArrayList<Address>();
for (Address a : garble) {
String[] splitted = a.toString().split(",");
if (splitted.length == 1) {
addressList.add(a);
continue;
}
for (String s : splitted) {
try {
addressList.add(new InternetAddress(getEmail(s),
getPersonal(s)));
} catch (UnsupportedEncodingException e) {
throw new MessagingException(
"Can not split the address because of an unsupported exception",
e);
}
}
}
return addressList.toArray(new InternetAddress[0]);
}
private String getEmail(String text) {
Matcher matcher = IN_BRACKET_FILTER.matcher(text);
if (!matcher.find()) {
return text;
}
if (matcher.groupCount() < 1) {
return text;
}
return matcher.group(1);
}
/**
* return PERSONAL. PERSONAL means String in double-quotations or out of
* angle-brackets. if there is not them, return empty string.
*
* @param text
* @return
*/
private String getPersonal(String text) {
// must be personal in quotations
Matcher quotation = IN_DOUBLE_QUOTATION_FILTER.matcher(text);
if (quotation.find()) {
if (quotation.groupCount() >= 1) {
return quotation.group(1);
}
}
// must be email in brackets
// must be personal out of brackets
Matcher nonBracket = OUT_OF_BRACKET_FILTER.matcher(text);
if (nonBracket.find()) {
if (nonBracket.groupCount() >= 1) {
return nonBracket.group(1).trim();
}
}
// If no " " and < > found, "text" can be an address or person name
try {
new InternetAddress(text);
return ""; // If it can be parsed to address, the person's name is
// empty
} catch (AddressException e) {
return text; // If it's not an address, it's person's name
}
}
// this is for encoded quote
private String getPersonalWithQuote(String text) {
// must be email in brackets
// must be personal out of brackets
Matcher nonBracket = OUT_OF_BRACKET_FILTER.matcher(text);
if (nonBracket.find()) {
if (nonBracket.groupCount() >= 1) {
return nonBracket.group(1).trim();
}
}
// If no " " and < > found, "text" can be an address or person name
try {
new InternetAddress(text);
return ""; // If it can be parsed to address, the person's name is
// empty
} catch (AddressException e) {
return text; // If it's not an address, it's person's name
}
}
private boolean isEvenNumberOfQuot(String text) {
int count = 0;
int indexOfQuot = -1;
while (true) {
indexOfQuot = text.indexOf("\"", indexOfQuot + 1);
if (indexOfQuot < 0) {
break;
}
count++;
}
if (count % 2 == 0) {
return true;
}
return false;
}
public boolean isBlankStrings(String[] array) {
if (array == null || array.length == 0) {
return true;
}
for (String s : array) {
if (!Strings.isNullOrEmpty(s)) {
return false;
}
}
return true;
}
}