##### Coverage Report - org.seasar.cubby.validator.validators.EmailValidator

Classes in this File | Line Coverage | Branch Coverage | Complexity
EmailValidator |
95% | 71/74
89% | 43/48
| 5.857142857142857;5.857

1 |  |
     /*
2 |  |
      * Copyright 2004-2009 the Seasar Foundation and the Others.
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License");
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 |  |
      * either express or implied. See the License for the specific language
14 |  |
      * governing permissions and limitations under the License.
15 |  |
      */
16 |  |
     package org.seasar.cubby.validator.validators;
17 |  |
18 |  |
     import java.util.ArrayList;
19 |  |
     import java.util.List;
20 |  |
     import java.util.regex.Matcher;
21 |  |
     import java.util.regex.Pattern;
22 |  |
23 |  |
     import org.seasar.cubby.action.MessageInfo;
24 |  |
     import org.seasar.cubby.internal.util.StringUtils;
25 |  |
     import org.seasar.cubby.validator.ScalarFieldValidator;
26 |  |
     import org.seasar.cubby.validator.ValidationContext;
27 |  |
28 |  |
     /**
29 |  |
      * <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC2821</a>
30 |  |
      * に準拠したメールアドレスかを検証します。
31 |  |
      * <p>
32 |  |
      * <table>
33 |  |
      * <caption>検証エラー時に設定するエラーメッセージ</caption> <tbody>
34 |  |
      * <tr>
35 |  |
      * <th scope="row">デフォルトのキー</th>
36 |  |
      * <td>valid.email</td>
37 |  |
      * </tr>
38 |  |
      * <tr>
39 |  |
      * <th scope="row">置換文字列</th>
40 |  |
      * <td>
41 |  |
      * <ol start="0">
42 |  |
      * <li>フィールド名</li>
43 |  |
      * </ol></td>
44 |  |
      * </tr>
45 |  |
      * </tbody>
46 |  |
      * </table>
47 |  |
      * </p>
48 |  |
      *
49 |  |
      * @see <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC2821</a>
50 |  |
      * @author agata
51 |  |
      * @author baba
52 |  |
      */
53 |  |
     public class EmailValidator implements ScalarFieldValidator {
54 |  |
55 |  |
             private static final String SPECIAL_CHARS = "\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";
56 |  |
             private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]";
57 |  |
             private static final String QUOTED_USER = "(\"[^\"]*\")";
58 |  |
             private static final String ATOM = VALID_CHARS + '+';
59 |  |
             private static final String WORD = "(" + ATOM + "|" + QUOTED_USER + ")";
60 |  |
61 |  |
             private static final String LEGAL_ASCII_PATTERN = "^[\\x00-\\x7F]+$";
62 |  |
             private static final String EMAIL_PATTERN = "^(.+)@(.+)$";
63 |  |
             private static final String IP_DOMAIN_PATTERN = "^(\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})$";
64 |  |
65 |  |
             private static final String USER_PATTERN = "^" + WORD + "(\\." + WORD
66 |  |
                             + ")*$";
67 |  |
             private static final String DOMAIN_PATTERN = "^" + ATOM + "(\\." + ATOM
68 |  |
                             + ")*$";
69 |  |
             private static final String ATOM_PATTERN = "(" + ATOM + ")";
70 |  |
71 |  |
             /**
72 |  |
              * メッセージキー。
73 |  |
              */
74 |  |
             private final String messageKey;
75 |  |
76 |  |
             /**
77 |  |
              * コンストラクタ
78 |  |
              */
79 |  |
             public EmailValidator() {
80 | 1 |
                     this("valid.email");
81 | 1 |
             }
82 |  |
83 |  |
             /**
84 |  |
              * メッセージキーを指定するコンストラクタ
85 |  |
              *
86 |  |
              * @param messageKey
87 |  |
              */
88 | 1 |
             public EmailValidator(final String messageKey) {
89 | 1 |
                     this.messageKey = messageKey;
90 | 1 |
             }
91 |  |
92 |  |
             /**
93 |  |
              * {@inheritDoc}
94 |  |
              */
95 |  |
             public void validate(final ValidationContext context, final Object value) {
96 | 16 |
                     if (value == null) {
97 | 1 |
                             return;
98 |  |
                     }
99 | 15 |
                     if (value instanceof String) {
100 | 15 |
                             final String email = (String) value;
101 | 15 |
                             if (StringUtils.isEmpty(email)) {
102 | 1 |
                                     return;
103 |  |
                             }
104 |  |
105 | 14 |
                             boolean match = !email.endsWith(".");
106 | 14 |
                             if (match) {
107 | 13 |
                                     final Pattern pattern = Pattern.compile(LEGAL_ASCII_PATTERN);
108 | 13 |
                                     final Matcher matchAsciiPat = pattern.matcher(email);
109 | 13 |
                                     match = matchAsciiPat.matches();
110 |  |
                             }
111 |  |
112 | 14 |
                             if (match) {
113 | 12 |
                                     final Pattern pattern = Pattern.compile(EMAIL_PATTERN);
114 | 12 |
                                     final Matcher matcher = pattern.matcher(email);
115 | 12 |
                                     match = matcher.find();
116 | 12 |
                                     if (match) {
117 | 10 |
                                             if (isValidUser(matcher.group(1))
118 |  |
                                                             && isValidDomain(matcher.group(2))) {
119 | 3 |
                                                     return;
120 |  |
                                             }
121 |  |
                                     }
122 |  |
                             }
123 |  |
                     }
124 |  |
125 | 11 |
                     final MessageInfo messageInfo = new MessageInfo();
126 | 11 |
                     messageInfo.setKey(this.messageKey);
127 | 11 |
                     context.addMessageInfo(messageInfo);
128 | 11 |
             }
129 |  |
130 |  |
             private boolean isValidDomain(final String domain) {
131 | 8 |
                     Pattern pattern = Pattern.compile(IP_DOMAIN_PATTERN);
132 | 8 |
                     final Matcher ipAddressMatcher = pattern.matcher(domain);
133 |  |
134 | 8 |
                     if (ipAddressMatcher.find()) {
135 | 2 |
                             if (isValidIpAddress(ipAddressMatcher)) {
136 | 1 |
                                     return true;
137 |  |
                             }
138 |  |
                     } else {
139 | 6 |
                             pattern = Pattern.compile(DOMAIN_PATTERN);
140 | 6 |
                             final Matcher domainMatcher = pattern.matcher(domain);
141 | 6 |
                             if (domainMatcher.matches()) {
142 | 6 |
                                     if (isValidSymbolicDomain(domain)) {
143 | 2 |
                                             return true;
144 |  |
                                     }
145 |  |
                             }
146 |  |
                     }
147 | 5 |
                     return false;
148 |  |
             }
149 |  |
150 |  |
             private boolean isValidUser(final String user) {
151 | 10 |
                     final Pattern pattern = Pattern.compile(USER_PATTERN);
152 | 10 |
                     final Matcher userMatcher = pattern.matcher(user);
153 | 10 |
                     return userMatcher.matches();
154 |  |
             }
155 |  |
156 |  |
             private boolean isValidIpAddress(final Matcher ipAddressMatcher) {
157 | 9 |
                     for (int i = 1; i <= 4; i++) {
158 | 8 |
                             final String ipSegment = ipAddressMatcher.group(i);
159 | 8 |
                             if (ipSegment == null || ipSegment.length() <= 0) {
160 | 0 |
                                     return false;
161 |  |
                             }
162 |  |
163 | 8 |
                             int iIpSegment = 0;
164 |  |
165 |  |
                             try {
166 | 8 |
                                     iIpSegment = Integer.parseInt(ipSegment);
167 | 0 |
                             } catch (final NumberFormatException e) {
168 | 0 |
                                     return false;
169 | 8 |
                             }
170 |  |
171 | 8 |
                             if (iIpSegment > 255) {
172 | 1 |
                                     return false;
173 |  |
                             }
174 |  |
175 |  |
                     }
176 | 1 |
                     return true;
177 |  |
             }
178 |  |
179 |  |
             private boolean isValidSymbolicDomain(final String domain) {
180 | 6 |
                     final List<String> domainSegments = new ArrayList<String>();
181 | 6 |
                     boolean match = true;
182 |  |
183 | 6 |
                     final Pattern pattern = Pattern.compile(ATOM_PATTERN);
184 |  |
                     String domainSegment;
185 | 6 |
                     String currentDomain = domain;
186 | 23 |
                     while (match) {
187 | 17 |
                             final Matcher atomMatcher = pattern.matcher(currentDomain);
188 | 17 |
                             match = atomMatcher.find();
189 | 17 |
                             if (match) {
190 | 11 |
                                     domainSegment = atomMatcher.group(1);
191 | 11 |
                                     domainSegments.add(domainSegment);
192 | 11 |
                                     final int domainSegmentLength = domainSegment.length() + 1;
193 | 11 |
                                     currentDomain = domainSegmentLength >= currentDomain.length() ? ""
194 |  |
                                                     : currentDomain.substring(domainSegmentLength);
195 |  |
                             }
196 | 17 |
                     }
197 |  |
198 | 6 |
                     final int size = domainSegments.size();
199 | 6 |
                     if (size > 0) {
200 | 6 |
                             final String end = domainSegments.get(size - 1);
201 | 6 |
                             if (end.length() < 2 || end.length() > 4) {
202 | 2 |
                                     return false;
203 |  |
                             }
204 |  |
                     }
205 |  |
206 | 4 |
                     if (size < 2) {
207 | 2 |
                             return false;
208 |  |
                     }
209 |  |
210 | 2 |
                     return true;
211 |  |
             }
212 |  |
213 |  |
     }