001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/util/tags/sakai_2-4-1/util-util/util/src/java/org/sakaiproject/util/Validator.java $
003: * $Id: Validator.java 14212 2006-09-05 01:31:34Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.util;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.sakaiproject.exception.IdInvalidException;
025:
026: /**
027: * <p>
028: * Validator is utility class that helps to validate stuff.
029: * </p>
030: */
031: public class Validator {
032: /** Our logger. */
033: private static Log M_log = LogFactory.getLog(Validator.class);
034:
035: /** These characters are not allowed in a resource id */
036: public static final String INVALID_CHARS_IN_RESOURCE_ID = "^/\\{}[]()%*?#&=\n\r\t\b\f";
037:
038: /** These characters are not allowed in a user id */
039: protected static final String INVALID_CHARS_IN_USER_ID = "^/\\%*?\n\r\t\b\f";
040:
041: protected static final String MAP_TO_A = "?";
042:
043: protected static final String MAP_TO_B = "§§";
044:
045: protected static final String MAP_TO_C = "?ĸĸ";
046:
047: protected static final String MAP_TO_E = "??žŽŽ";
048:
049: protected static final String MAP_TO_I = "";
050:
051: protected static final String MAP_TO_L = "ŖŖ";
052:
053: protected static final String MAP_TO_N = "";
054:
055: protected static final String MAP_TO_O = "
";
056:
057: protected static final String MAP_TO_U = "?";
058:
059: protected static final String MAP_TO_Y = "Ø´??";
060:
061: protected static final String MAP_TO_X = "???¤Šģ¨ą?ĢĩĻĀ?";
062:
063: /**
064: * These characters are allowed; but if escapeResourceName() is called, they are escaped (actually, removed) Certain characters cause problems with filenames in certain OSes - so get rid of these characters in filenames
065: */
066: protected static final String ESCAPE_CHARS_IN_RESOURCE_ID = ";'\"";
067:
068: protected static final String INVALID_CHARS_IN_ZIP_ENTRY = "/\\%:*?'\"";
069:
070: protected static final String INVALID_MSG = " cannot contain any of the following characters: / \\ % * ? # & = { } ( ) ^";
071:
072: protected static final String BLANK_MSG = " cannot be blank";
073:
074: /** These characters are escaped when making a URL */
075: // protected static final String ESCAPE_URL = "#%?&='\"+ ";
076: // not '/' as that is assumed to be part of the path
077: protected static final String ESCAPE_URL = "$&+,:;=?@ '\"<>#%{}|\\^~[]`";
078:
079: /**
080: * These can't be encoded in URLs safely even using %nn notation, so encode them using our own custom URL encoding, which the ParameterParser decodes
081: */
082: protected static final String ESCAPE_URL_SPECIAL = "^?;";
083:
084: /** Valid special email local id characters (- those that are invalid resource ids) */
085: protected static final String VALID_EMAIL = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'*+-=?^_`{|}~.";
086:
087: /**
088: * Escape a plaintext string so that it can be output as part of an HTML document. Amperstand, greater-than, less-than, newlines, etc, will be escaped so that they display (instead of being interpreted as formatting).
089: *
090: * @param value
091: * The string to escape.
092: * @return value fully escaped for HTML.
093: */
094: public static String escapeHtml(String value) {
095: return FormattedText.escapeHtml(value, true);
096: }
097:
098: /**
099: * Escape a plaintext string so that it can be output as part of an HTML document, except that newlines are NOT escaped and therefore are treated as whitespace instead of displaying as line-breaks. Amperstand, greater-than, less-than, etc, will be
100: * escaped so that they display (instead of being interpreted as formatting).
101: *
102: * @param value
103: * The string to escape.
104: * @return value fully escaped for HTML.
105: */
106: public static String escapeHtmlSupressNewlines(String value) {
107: return FormattedText.escapeHtml(value, false);
108: }
109:
110: /**
111: * Escape plaintext for display inside a plain textarea.
112: */
113: public static String escapeHtmlTextarea(String value) {
114: return FormattedText.escapeHtml(value, false);
115: }
116:
117: /**
118: * Escape HTML-formatted text in preparation to include it in an HTML document.
119: */
120: public static String escapeHtmlFormattedText(String value) {
121: return FormattedText.escapeHtmlFormattedText(value);
122: }
123:
124: /**
125: * Escape HTML-formatted text in preparation to include it in an HTML document, except that HTML line breaks ("<br />") will be supressed (removed).
126: */
127: public static String escapeHtmlFormattedTextSupressNewlines(
128: String value) {
129: return FormattedText
130: .escapeHtmlFormattedTextSupressNewlines(value);
131: }
132:
133: /**
134: * Escapes the given HTML-formatted text for editing within the WYSIWYG editor. All HTML meta-characters in the string (such as amperstand, less-than, etc), will be escaped.
135: *
136: * @param value
137: * The formatted text to escape
138: * @return The string to use as the value of the formatted textarea widget
139: */
140: public static String escapeHtmlFormattedTextarea(String value) {
141: return FormattedText.escapeHtmlFormattedTextarea(value);
142: }
143:
144: /**
145: * escapeHtml(), but also fix the case where we start with © and treat it as copyright (c) Note: ResourcesAction used to (before 1.1.05) place this as the copyright symbol. -ggolden
146: */
147: public static String escapeHtmlFixCopyright(String value) {
148: if (value.startsWith("©")) {
149: value = "copyright (c)" + value.substring(6);
150: }
151:
152: return escapeHtml(value);
153:
154: } // escapeHtmlFixCopyright
155:
156: /**
157: * Return a string based on id that is fully escaped using URL rules, using a UTF-8 underlying encoding.
158: *
159: * @param id
160: * The string to escape.
161: * @return id fully escaped using URL rules.
162: */
163: public static String escapeUrl(String id) {
164: if (id == null)
165: return "";
166: id = id.trim();
167: try {
168: // convert the string to bytes in UTF-8
169: byte[] bytes = id.getBytes("UTF-8");
170:
171: StringBuffer buf = new StringBuffer();
172: for (int i = 0; i < bytes.length; i++) {
173: byte b = bytes[i];
174: // escape ascii control characters, ascii high bits, specials
175: if (ESCAPE_URL_SPECIAL.indexOf((char) b) != -1) {
176: buf.append("^^x"); // special funky way to encode bad URL characters - ParameterParser will decode it
177: buf.append(toHex(b));
178: buf.append('^');
179: } else if ((ESCAPE_URL.indexOf((char) b) != -1)
180: || (b <= 0x1F) || (b == 0x7F) || (b >= 0x80)) {
181: buf.append("%");
182: buf.append(toHex(b));
183: } else {
184: buf.append((char) b);
185: }
186: }
187:
188: String rv = buf.toString();
189: return rv;
190: } catch (Exception e) {
191: M_log.warn("Validator.escapeUrl: ", e);
192: return id;
193: }
194:
195: } // escapeUrl
196:
197: /**
198: * Return a string based on id that is fully escaped using Resource name validity rules.
199: *
200: * @param id
201: * The string to escape.
202: * @return id fully escaped using Resource name validity rules.
203: */
204: public static String escapeResourceName(String id) {
205: if (id == null)
206: return "";
207: id = id.trim();
208: try {
209: StringBuffer buf = new StringBuffer();
210: for (int i = 0; i < id.length(); i++) {
211: char c = id.charAt(i);
212: if (MAP_TO_A.indexOf(c) >= 0) {
213: buf.append('a');
214: } else if (MAP_TO_E.indexOf(c) >= 0) {
215: buf.append('e');
216: } else if (MAP_TO_I.indexOf(c) >= 0) {
217: buf.append('i');
218: } else if (MAP_TO_O.indexOf(c) >= 0) {
219: buf.append('o');
220: } else if (MAP_TO_U.indexOf(c) >= 0) {
221: buf.append('u');
222: } else if (MAP_TO_Y.indexOf(c) >= 0) {
223: buf.append('y');
224: } else if (MAP_TO_N.indexOf(c) >= 0) {
225: buf.append('n');
226: } else if (MAP_TO_B.indexOf(c) >= 0) {
227: buf.append('b');
228: } else if (MAP_TO_C.indexOf(c) >= 0) {
229: buf.append('c');
230: } else if (MAP_TO_L.indexOf(c) >= 0) {
231: buf.append('l');
232: } else if (MAP_TO_X.indexOf(c) >= 0) {
233: buf.append('x');
234: } else {
235: while (c > '\176') {
236: c = (char) (c - '\117');
237: }
238: while (c < '\040') {
239: c = (char) (c + '\040');
240: }
241: if (INVALID_CHARS_IN_RESOURCE_ID.indexOf(c) >= 0
242: || ESCAPE_CHARS_IN_RESOURCE_ID.indexOf(c) >= 0) {
243: buf.append('_');
244: } else {
245: buf.append(c);
246: }
247: }
248: }
249:
250: String rv = buf.toString();
251: return rv;
252: } catch (Exception e) {
253: M_log.warn("Validator.escapeResourceName: ", e);
254: return id;
255: }
256:
257: } // escapeResourceName
258:
259: /**
260: * Return a string based on id that is fully escaped the question mark.
261: *
262: * @param id
263: * The string to escape.
264: * @return id fully escaped question mark.
265: */
266: public static String escapeQuestionMark(String id) {
267: if (id == null)
268: return "";
269: try {
270: StringBuffer buf = new StringBuffer();
271: for (int i = 0; i < id.length(); i++) {
272: char c = id.charAt(i);
273: if (c == '?') {
274: buf.append('_');
275: } else {
276: buf.append(c);
277: }
278: }
279:
280: String rv = buf.toString();
281: return rv;
282: } catch (Exception e) {
283: M_log.warn("Validator.escapeQuestionMark: ", e);
284: return id;
285: }
286:
287: } // escapeQuestionMark
288:
289: /**
290: * Return a string based on id that is fully escaped to create a zip entry
291: *
292: * @param id
293: * The string to escape.
294: * @return id fully escaped to create a zip entry
295: */
296: public static String escapeZipEntry(String id) {
297: if (id == null)
298: return "";
299: try {
300: StringBuffer buf = new StringBuffer();
301: for (int i = 0; i < id.length(); i++) {
302: char c = id.charAt(i);
303: if (INVALID_CHARS_IN_ZIP_ENTRY.indexOf(c) != -1) {
304: buf.append('_');
305: } else {
306: buf.append(c);
307: }
308: }
309:
310: String rv = buf.toString();
311: return rv;
312: } catch (Exception e) {
313: M_log.warn("Validator.escapeZipEntry: ", e);
314: return id;
315: }
316:
317: } // escapeZipEntry
318:
319: /**
320: * Return a string based on value that is safe to place into a javascript value that is in single quiotes.
321: *
322: * @param value
323: * The string to escape.
324: * @return value escaped.
325: */
326: public static String escapeJsQuoted(String value) {
327: if (value == null)
328: return "";
329: try {
330: StringBuffer buf = new StringBuffer();
331: for (int i = 0; i < value.length(); i++) {
332: char c = value.charAt(i);
333:
334: // a single quote must be escaped with a leading backslash
335: if (c == '\'') {
336: buf.append("\\'");
337: }
338:
339: // a backslash must be escaped with another backslash
340: else if (c == '\\') {
341: buf.append("\\\\");
342: }
343:
344: else {
345: buf.append(c);
346: }
347: }
348:
349: String rv = buf.toString();
350: return rv;
351: } catch (Exception e) {
352: M_log.warn("Validator.escapeJsQuoted: ", e);
353: return value;
354: }
355:
356: } // escapeJsQuoted
357:
358: /**
359: * Return a string based on value that is safe to place into a sql statement: sql statements use the single quote, and this must be doubled as an escape.
360: *
361: * @param value
362: * The string to escape.
363: * @return value escaped.
364: */
365: public static String escapeSql(String value) {
366: if (value == null)
367: return "";
368: try {
369: StringBuffer buf = new StringBuffer();
370: for (int i = 0; i < value.length(); i++) {
371: char c = value.charAt(i);
372: if (c == '\'') {
373: buf.append("''");
374: } else {
375: buf.append(c);
376: }
377: }
378:
379: String rv = buf.toString();
380: return rv;
381: } catch (Exception e) {
382: M_log.warn("Validator.escapeSql: ", e);
383: return value;
384: }
385:
386: } // escapeSql
387:
388: /**
389: * Return a string based on value that is safe to place into a javascript / html identifier: anything not alphanumeric change to 'x'. If the first character is not alphabetic, a letter 'i' is prepended.
390: *
391: * @param value
392: * The string to escape.
393: * @return value fully escaped using javascript / html identifier rules.
394: */
395: public static String escapeJavascript(String value) {
396: if (value == null || value == "")
397: return "";
398: try {
399: StringBuffer buf = new StringBuffer();
400:
401: // prepend 'i' if first character is not a letter
402: if (!java.lang.Character.isLetter(value.charAt(0))) {
403: buf.append("i");
404: }
405:
406: // change non-alphanumeric characters to 'x'
407: for (int i = 0; i < value.length(); i++) {
408: char c = value.charAt(i);
409: if (!java.lang.Character.isLetterOrDigit(c)) {
410: buf.append("x");
411: } else {
412: buf.append(c);
413: }
414: }
415:
416: String rv = buf.toString();
417: return rv;
418: } catch (Exception e) {
419: M_log.warn("Validator.escapeJavascript: ", e);
420: return value;
421: }
422:
423: } // escapeJavascript
424:
425: /**
426: * Check for a valid user id.
427: *
428: * @exception IdInvalidException
429: * if the id is invalid.
430: */
431: public static boolean checkUserId(String id) {
432: // the rules:
433: // Null is rejected
434: // all blank is rejected
435: // INVALID_CHARS_IN_USER_ID characters are rejected
436:
437: if (id == null)
438: return false;
439: if (id.trim().length() == 0)
440: return false;
441:
442: // we must reject certain characters that we cannot even escape and get into Tomcat via a URL
443: for (int i = 0; i < id.length(); i++) {
444: if (INVALID_CHARS_IN_USER_ID.indexOf(id.charAt(i)) != -1)
445: return false;
446: }
447:
448: return true;
449:
450: } // checkUserId
451:
452: /**
453: * Check for a valid resource id.
454: *
455: * @return true if valid, false if not
456: */
457: public static boolean checkResourceId(String id) {
458: // the rules:
459: // Null is rejected
460: // all blank is rejected
461: // INVALID_CHARS_IN_RESOURCE_ID characters are rejected
462:
463: if (id == null)
464: return false;
465: if (id.trim().length() == 0)
466: return false;
467:
468: // we must reject certain characters that we cannot even escape and get into Tomcat via a URL
469: for (int i = 0; i < id.length(); i++) {
470: if (INVALID_CHARS_IN_RESOURCE_ID.indexOf(id.charAt(i)) != -1)
471: return false;
472: }
473:
474: return true;
475:
476: } // checkResourceId
477:
478: /**
479: * Is this a valid local part of an email id?
480: */
481: public static boolean checkEmailLocal(String id) {
482: // rules based on rfc2882, but a bit more conservative
483:
484: for (int i = 0; i < id.length(); i++) {
485: if (VALID_EMAIL.indexOf(id.charAt(i)) == -1)
486: return false;
487: }
488:
489: return true;
490:
491: } // checkEmailLocal
492:
493: /**
494: * Isolate and return just the file name part of a full drive and path file name.
495: *
496: * @param fullName
497: * The full file name from a local os file system (mac, unix, windoze)
498: * @return Just the name (and extension) of the file, without the drive or path.
499: */
500: public static String getFileName(String fullName) {
501: // examples: windows: c:\this\that\me.doc
502: // unix: /usr/local/dev/test.txt
503: // mac:? one:two:three:four
504: // so... just take the last characters back till we see a \ or / or :
505: StringBuffer buf = new StringBuffer();
506: int index = fullName.length() - 1;
507: while (index >= 0) {
508: char c = fullName.charAt(index--);
509: if ((c == '\\') || (c == '/') || (c == ':'))
510: break;
511: buf.insert(0, c);
512: }
513:
514: return buf.toString();
515:
516: } // getFileName
517:
518: /**
519: * Put the dividor (comma) inside the size string, for example, 1,003 for 1003
520: *
521: * @param size
522: * The string of size number
523: * @return The size string with the dividor added
524: */
525: public static String getFileSizeWithDividor(String size) {
526: StringBuffer newSize = new StringBuffer(size);
527:
528: int length = size.length();
529: int index = size.length();
530:
531: while (index > 3) {
532: index = index - 3;
533: newSize.insert(index, ",");
534: }
535:
536: return newSize.toString();
537: }
538:
539: /**
540: * Isolate and return just the file extension part of a full drive and path file name.
541: *
542: * @param fullName
543: * The full file name from a local os file system (mac, unix, windoze)
544: * @return Just the extension of the file, to the right of the dot, not including the dot, or blank if none.
545: */
546: public static String getFileExtension(String fullName) {
547: // just take from the last dot to the end, or return "" if there's no dot.
548: int index = fullName.lastIndexOf('.');
549: if (index == -1)
550: return "";
551:
552: return fullName.substring(index + 1);
553:
554: } // getFileExtension
555:
556: /**
557: * Determine whether a file resource should be opened in the current window or a new window.
558: *
559: * @param contentType
560: * The content type to check
561: * @return A string identifying the window in which to open the resource: "_self" to open the resource in the current window, "_blank" for a new window, or an empty string if the resource is not a file.
562: */
563: public static String getResourceTarget(String contentType) {
564: // we will open a new window unless...
565: String rv = "_blank";
566:
567: // get the resource's type
568: if (contentType != null) {
569: // if the browser will not inline, but mark as attachments, let's not open a new window
570: if (!letBrowserInline(contentType)) {
571: rv = "_self";
572: }
573: }
574:
575: return rv;
576:
577: } // getResourceTarget
578:
579: /**
580: * Is this a mime type that the browser can handle inline, in a browser window? If so, links to this type should be to a _blank, and content-disposition should be inline. If not, links to this type should be to _self, and content-disposition should be
581: * attachment.
582: *
583: * @param type
584: * The mime type to check.
585: * @return true if this type of resource the browser can handle in line, false if not.
586: */
587: public static boolean letBrowserInline(String type) {
588: if (type == null)
589: return false;
590:
591: String lType = type.toLowerCase();
592:
593: if (lType.startsWith("text/"))
594: return true;
595: if (lType.startsWith("image/"))
596: return true;
597: if (lType.equals("application/pdf"))
598: return true;
599: if (lType.equals("application/x-osp"))
600: return true;
601: // checks for VRML file MIME types:x-world/x-vrml, model/vrml, application/x-blaxxunCC3D, application/x-blaxxunCC3Dpro, application/x-CC3D
602: // need to check for any other MIME types which can be opened by browser plug-ins? %%%zqian
603: if (lType.indexOf("vrml") != -1 || lType.indexOf("CC3D") != -1)
604: return true;
605:
606: return false;
607:
608: } // letBrowserInline
609:
610: /**
611: * Limit the string to a certain number of characters, adding "..." if it was truncated.
612: *
613: * @param value
614: * The string to limit.
615: * @param the
616: * length to limit to (as an Integer).
617: * @return The limited string.
618: */
619: public static String limit(String value, Integer length) {
620: return limit(value, length.intValue());
621:
622: } // limit
623:
624: /**
625: * Limit the string to a certain number of characters, adding "..." if it was truncated
626: *
627: * @param value
628: * The string to limit.
629: * @param the
630: * length to limit to (as an int).
631: * @return The limited string.
632: */
633: public static String limit(String value, int length) {
634: StringBuffer buf = new StringBuffer(value);
635: if (buf.length() > length) {
636: buf.setLength(length);
637: buf.append("...");
638: }
639:
640: return buf.toString();
641:
642: } // limit
643:
644: /**
645: * Limit the formatted to a certain number of DISPLAYED characters, adding "..." if it was truncated. For example, <xmp>trim("Hello \n<b>World</b>!", 7)</xmp> returns <xmp>"Hello \n<b>W</b>..."</xmp>
646: *
647: * @param value
648: * The formatted text to limit.
649: * @param the
650: * length to limit to (as an int).
651: * @return The limited string.
652: */
653: public static String limitFormattedText(String value, int length) {
654: StringBuffer ret = new StringBuffer();
655: value = FormattedText
656: .escapeHtmlFormattedTextSupressNewlines(value);
657: boolean didTrim = FormattedText.trimFormattedText(value,
658: length, ret);
659: if (didTrim)
660: ret.append("...");
661: return ret.toString();
662: }
663:
664: /**
665: * Clean the user input string of strange newlines, etc.
666: *
667: * @param value
668: * The user input string.
669: * @return value cleaned of string newlines, etc.
670: */
671: public static String cleanInput(String value) {
672: if (value == null)
673: return null;
674: if (value.length() == 0)
675: return value;
676:
677: final int len = value.length();
678: StringBuffer buf = new StringBuffer();
679:
680: for (int i = 0; i < len; i++) {
681: char c = value.charAt(i);
682: char next = 0;
683: if (i + 1 < len)
684: next = value.charAt(i + 1);
685:
686: switch (c) {
687: case '\r': {
688: // detect CR LF, make it a \n
689: if (next == '\n') {
690: buf.append('\n');
691: // eat the next character
692: i++;
693: } else {
694: buf.append(c);
695: }
696:
697: }
698: break;
699:
700: default: {
701: buf.append(c);
702: }
703: }
704: }
705:
706: if (buf.charAt(buf.length() - 1) == '\n') {
707: buf.setLength(buf.length() - 1);
708: }
709:
710: return buf.toString();
711:
712: } // cleanInput
713:
714: /**
715: * Clean the string parameter of all newlines (replace with space character) and trim leading and trailing spaces
716: *
717: * @param value
718: * The user input string.
719: * @return value cleaned of newlines, etc.
720: */
721: public static String stripAllNewlines(String value) {
722: if (value == null)
723: return null;
724: value = value.trim();
725: if (value.length() == 0)
726: return value;
727:
728: final int len = value.length();
729: StringBuffer buf = new StringBuffer();
730:
731: for (int i = 0; i < len; i++) {
732: char c = value.charAt(i);
733: char next = 0;
734: if (i + 1 < len)
735: next = value.charAt(i + 1);
736:
737: switch (c) {
738: case '\n':
739: case '\r': {
740: buf.append(' ');
741: }
742: break;
743:
744: default: {
745: buf.append(c);
746: }
747: }
748: }
749:
750: return buf.toString();
751:
752: } // stripAllNewlines
753:
754: /**
755: * Returns a hex representation of a byte.
756: *
757: * @param b
758: * The byte to convert to hex.
759: * @return The 2-digit hex value of the supplied byte.
760: */
761: private static final String toHex(byte b) {
762:
763: char ret[] = new char[2];
764:
765: ret[0] = hexDigit((b >>> 4) & (byte) 0x0F);
766: ret[1] = hexDigit((b >>> 0) & (byte) 0x0F);
767:
768: return new String(ret);
769: }
770:
771: /**
772: * Returns the hex digit cooresponding to a number between 0 and 15.
773: *
774: * @param i
775: * The number to get the hex digit for.
776: * @return The hex digit cooresponding to that number.
777: * @exception java.lang.IllegalArgumentException
778: * If supplied digit is not between 0 and 15 inclusive.
779: */
780: private static final char hexDigit(int i) {
781:
782: switch (i) {
783: case 0:
784: return '0';
785: case 1:
786: return '1';
787: case 2:
788: return '2';
789: case 3:
790: return '3';
791: case 4:
792: return '4';
793: case 5:
794: return '5';
795: case 6:
796: return '6';
797: case 7:
798: return '7';
799: case 8:
800: return '8';
801: case 9:
802: return '9';
803: case 10:
804: return 'A';
805: case 11:
806: return 'B';
807: case 12:
808: return 'C';
809: case 13:
810: return 'D';
811: case 14:
812: return 'E';
813: case 15:
814: return 'F';
815: }
816:
817: throw new IllegalArgumentException("Invalid digit:" + i);
818: }
819:
820: /**
821: * Validate whether the date input is valid
822: */
823: public static boolean checkDate(int day, int month, int year) {
824: // Is date valid for month?
825: if (month == 2) {
826: // Check for leap year
827: if (((year % 4 == 0) && (year % 100 != 0))
828: || (year % 400 == 0)) {
829: // leap year
830: if (day > 29) {
831: return false;
832: }
833: } else {
834: // normal year
835: if (day > 28) {
836: return false;
837: }
838: }
839: } else if ((month == 4) || (month == 6) || (month == 9)
840: || (month == 11)) {
841: if (day > 30) {
842: return false;
843: }
844: }
845:
846: return true;
847: }
848: }
|