You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fx-dev@ws.apache.org by we...@apache.org on 2005/05/01 20:21:17 UTC

cvs commit: ws-fx/wss4j/src/org/apache/ws/security/util XmlSchemaDateFormat.java

werner      2005/05/01 11:21:17

  Added:       wss4j/src/org/apache/ws/security/util
                        XmlSchemaDateFormat.java
  Log:
  New class to process XML Schema compliant timestamps.
  Thanks to Ian. P. Springer for the code.
  
  Revision  Changes    Path
  1.1                  ws-fx/wss4j/src/org/apache/ws/security/util/XmlSchemaDateFormat.java
  
  Index: XmlSchemaDateFormat.java
  ===================================================================
  /*
   * Copyright  2003-2005 The Apache Software Foundation.
   *
   *  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 org.apache.ws.security.util;
  
  import java.text.DateFormat;
  import java.text.FieldPosition;
  import java.text.ParsePosition;
  import java.text.SimpleDateFormat;
  import java.text.ParseException;
  import java.util.Date;
  import java.util.TimeZone;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  /**
   * A {@link DateFormat} for the format of the dateTime simpleType as specified in the
   * XML Schema specification. See <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">
   * XML Schema Part 2: Datatypes, W3C Recommendation 02 May 2001, Section 3.2.7.1</a>.
   *
   * @author Ian P. Springer
   * @author Werner Dittmann
   */
  public class XmlSchemaDateFormat extends DateFormat {
  	/**
  	 * Logger.
  	 */
  	private static Log log = LogFactory.getLog(XmlSchemaDateFormat.class);
  
  	private static boolean doDebug = false;
  
  	static {
  		doDebug = log.isDebugEnabled();
  	}
  
  	/**
  	 * Message retriever.
  	 */
  	//   private static final MessageRetriever MSG = ResourceKeys.MSG;
  	/**
  	 * DateFormat for Zulu (UTC) form of an XML Schema dateTime string.
  	 */
  	private static final DateFormat DATEFORMAT_XSD_ZULU = new SimpleDateFormat(
  			"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
  
  	static {
  		DATEFORMAT_XSD_ZULU.setTimeZone(TimeZone.getTimeZone("UTC"));
  	}
  
  	/**
  	 * This method was snarfed from <tt>org.apache.axis.encoding.ser.CalendarDeserializer</tt>,
  	 * which was written by Sam Ruby (rubys@us.ibm.com) and Rich Scheuerle (scheu@us.ibm.com).
  	 * Better error reporting was added.
  	 *
  	 * @see DateFormat#parse(java.lang.String)
  	 */
  	public Date parse(String src, ParsePosition parse_pos) {
  		Date date;
  
  		// validate fixed portion of format
  		int index = 0;
  		try {
  			if (src != null) {
  				if ((src.charAt(0) == '+') || (src.charAt(0) == '-')) {
  					src = src.substring(1);
  				}
  
  				if (src.length() < 19) {
  					parse_pos.setIndex(src.length() - 1);
  					handleParseError(parse_pos, "TOO_FEW_CHARS");
  				}
  				validateChar(src, parse_pos, index = 4, '-', "EXPECTED_DASH");
  				validateChar(src, parse_pos, index = 7, '-', "EXPECTED_DASH");
  				validateChar(src, parse_pos, index = 10, 'T', "EXPECTED_CAPITAL_T");
  				validateChar(src, parse_pos, index = 13, ':', "EXPECTED_COLON_IN_TIME");
  				validateChar(src, parse_pos, index = 16, ':', "EXPECTED_COLON_IN_TIME");
  			}
  
  			// convert what we have validated so far
  			try {
  				synchronized (DATEFORMAT_XSD_ZULU) {
  					date = DATEFORMAT_XSD_ZULU.parse((src == null) ? null
  							: (src.substring(0, 19) + ".000Z"));
  				}
  			} catch (Exception e) {
  				throw new NumberFormatException(e.toString());
  			}
  
  			index = 19;
  
  			// parse optional milliseconds
  			if (src != null) {
  				if ((index < src.length()) && (src.charAt(index) == '.')) {
  					int milliseconds = 0;
  					int start = ++index;
  
  					while ((index < src.length())
  							&& Character.isDigit(src.charAt(index))) {
  						index++;
  					}
  
  					String decimal = src.substring(start, index);
  
  					if (decimal.length() == 3) {
  						milliseconds = Integer.parseInt(decimal);
  					} else if (decimal.length() < 3) {
  						milliseconds = Integer.parseInt((decimal + "000")
  								.substring(0, 3));
  					} else {
  						milliseconds = Integer
  								.parseInt(decimal.substring(0, 3));
  
  						if (decimal.charAt(3) >= '5') {
  							++milliseconds;
  						}
  					}
  
  					// add milliseconds to the current date
  					date.setTime(date.getTime() + milliseconds);
  				}
  
  				// parse optional timezone
  				if (((index + 5) < src.length())
  						&& ((src.charAt(index) == '+') || (src.charAt(index) == '-'))) {
  					validateCharIsDigit(src, parse_pos, index + 1, "EXPECTED_NUMERAL");
  					validateCharIsDigit(src, parse_pos, index + 2, "EXPECTED_NUMERAL");
  					validateChar(src, parse_pos, index + 3, ':', "EXPECTED_COLON_IN_TIMEZONE");
  					validateCharIsDigit(src, parse_pos, index + 4, "EXPECTED_NUMERAL");
  					validateCharIsDigit(src, parse_pos, index + 5, "EXPECTED_NUMERAL");
  
  					final int hours = (((src.charAt(index + 1) - '0') * 10) + src
  							.charAt(index + 2)) - '0';
  					final int mins = (((src.charAt(index + 4) - '0') * 10) + src
  							.charAt(index + 5)) - '0';
  					int millisecs = ((hours * 60) + mins) * 60 * 1000;
  
  					// subtract millisecs from current date to obtain GMT
  					if (src.charAt(index) == '+') {
  						millisecs = -millisecs;
  					}
  
  					date.setTime(date.getTime() + millisecs);
  					index += 6;
  				}
  
  				if ((index < src.length()) && (src.charAt(index) == 'Z')) {
  					index++;
  				}
  
  				if (index < src.length()) {
  					handleParseError(parse_pos, "TOO_MANY_CHARS");
  				}
  			}
  		} catch (ParseException pe) {
  			log.error(pe.toString());
  			index = 0; // IMPORTANT: this tells DateFormat.parse() to throw a ParseException
  			parse_pos.setErrorIndex(index);
  			date = null;
  		}
  		parse_pos.setIndex(index);
  		return (date);
  	}
  
  	/**
  	 * @see DateFormat#format(java.util.Date)
  	 */
  	public StringBuffer format(Date date, StringBuffer append_buf,
  			FieldPosition field_pos) {
  		String str;
  
  		synchronized (DATEFORMAT_XSD_ZULU) {
  			str = DATEFORMAT_XSD_ZULU.format(date);
  		}
  
  		if (append_buf == null) {
  			append_buf = new StringBuffer();
  		}
  
  		append_buf.append(str);
  
  		return (append_buf);
  	}
  
  	private void validateChar(String str, ParsePosition parse_pos, int index,
  			char expected, String error_reason) throws ParseException {
  		if (str.charAt(index) != expected) {
  			handleParseError(parse_pos, error_reason);
  		}
  	}
  
  	private void validateCharIsDigit(String str, ParsePosition parse_pos,
  			int index, String error_reason) throws ParseException {
  		if (!Character.isDigit(str.charAt(index))) {
  			handleParseError(parse_pos, error_reason);
  		}
  	}
  
  	private void handleParseError(ParsePosition parse_pos, String error_reason)
  			throws ParseException {
  		throw new ParseException("INVALID_XSD_DATETIME", parse_pos.getErrorIndex());
  	}
  
  }