/* * eXist Mail Module Extension MessageListFunctions * Copyright (C) 2006-09 Adam Retter <adam.retter@devon.gov.uk> * www.adamretter.co.uk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.xquery.modules.mail; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import javax.mail.Address; import javax.mail.FetchProfile; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Header; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.search.AndTerm; import javax.mail.search.BodyTerm; import javax.mail.search.ComparisonTerm; import javax.mail.search.FlagTerm; import javax.mail.search.FromStringTerm; import javax.mail.search.HeaderTerm; import javax.mail.search.NotTerm; import javax.mail.search.OrTerm; import javax.mail.search.ReceivedDateTerm; import javax.mail.search.RecipientStringTerm; import javax.mail.search.SearchTerm; import javax.mail.search.SentDateTerm; import javax.mail.search.SubjectTerm; import org.apache.log4j.Logger; import org.exist.dom.QName; import org.exist.memtree.MemTreeBuilder; import org.exist.xquery.BasicFunction; import org.exist.xquery.Cardinality; import org.exist.xquery.FunctionSignature; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import org.exist.xquery.value.FunctionParameterSequenceType; import org.exist.xquery.value.FunctionReturnSequenceType; import org.exist.xquery.value.IntegerValue; import org.exist.xquery.value.NodeValue; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.SequenceType; import org.exist.xquery.value.Type; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * eXist Mail Module Extension GetMessageList * * Get a mail store * * @author Andrzej Taramina <andrzej@chaeron.com> * @serial 2009-03-12 * @version 1.3 * * @see org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext, org.exist.xquery.FunctionSignature) */ public class MessageListFunctions extends BasicFunction { protected static final Logger logger = Logger.getLogger(MessageListFunctions.class); public final static FunctionSignature signatures[] = { new FunctionSignature( new QName( "get-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ), "Returns a message list of all messages in a folder.", new SequenceType[] { new FunctionParameterSequenceType( "mail-folder-handle", Type.INTEGER, Cardinality.EXACTLY_ONE, "The mail folder handle retrieved from mail:get-mail-folder()" ) }, new FunctionReturnSequenceType( Type.LONG, Cardinality.ZERO_OR_ONE, "an xs:long representing the message list handle." ) ), new FunctionSignature( new QName( "search-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ), "Searches messages in a folder. " + "Search terms are of the form <searchTerm type=\"xxx\">...</searchTerm>. Valid types include: not, and, or, from, subject, body, recipient, header, flag, sent, received. " + "<searchTerm type=\"not\"> requires a single nested child search term. <searchTerm type=\"and\"> and <searchTerm type=\"or\"> must have one or more nested child search terms. " + "<searchTerm type=\"from\" pattern=\"pat\">, <searchTerm type=\"subject\" pattern=\"pat\"> and <searchTerm type=\"body\" pattern=\"pat\"> require a pattern attribute and will search for a substring that matches the pattern. " + "<searchTerm type=\"recipient\" pattern=\"pat\" recipientType=\"to|cc|bcc\"> requires pattern and recipientType attributes. " + "<searchTerm type=\"header\" pattern=\"pat\" name=\"Content-Type\"> requires pattern and name attributes. " + "<searchTerm type=\"flag\" flag=\"answered|deleted|draft|recent|seen\" value=\"true|false\"> requires flag and value attributes. " + "<searchTerm type=\"sent\" comparison=\"eq|gt|ge|lt|le|ne\" format=\"format\" date=\"date\"> and <searchTerm type=\"received\" comparison=\"eq|gt|ge|lt|le|ne\" format=\"format\" date=\"date\"> require comparison, format and date attributes. " + "The format string should conform to Java SimpleDateFormat specifications and the date string must conform to the specified format string.", new SequenceType[] { new FunctionParameterSequenceType( "mail-folder-handle", Type.INTEGER, Cardinality.EXACTLY_ONE, "The mail folder handle retrieved from mail:get-mail-folder()" ), new FunctionParameterSequenceType( "search-parameters", Type.ELEMENT, Cardinality.EXACTLY_ONE, "The xml fragment defining the search terms" ) }, new FunctionReturnSequenceType( Type.LONG, Cardinality.ZERO_OR_ONE, "an xs:long representing the message list handle." ) ), new FunctionSignature( new QName( "get-message-list-as-xml", MailModule.NAMESPACE_URI, MailModule.PREFIX ), "Returns a message list of all messages in a folder as XML. If there are no messages in the list, an empty sequence will be returned", new SequenceType[] { new FunctionParameterSequenceType( "message-list-handle", Type.INTEGER, Cardinality.EXACTLY_ONE, "The message list handle retrieved from mail:get-message-list() or mail:search-message-list()" ), new FunctionParameterSequenceType( "include-headers", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "A boolean specifying whether to include message headers" ) }, new FunctionReturnSequenceType( Type.ELEMENT, Cardinality.ZERO_OR_ONE, "the list of all messages in a folder as XML" ) ), new FunctionSignature( new QName( "close-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ), "Closes a message list.", new SequenceType[] { new FunctionParameterSequenceType( "message-list-handle", Type.INTEGER, Cardinality.EXACTLY_ONE, "The message list handle retrieved from mail:get-message-list() or mail:search-message-list()" ) }, new SequenceType( Type.ITEM, Cardinality.EMPTY ) ) }; private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; private static final String PREFETCH_HEADERS[] = { "Return-Path", "Delivered-To", "Received", "Date", "From", "To", "Message-ID", "Subject", "MIME-Version", "Content-Type", "Content-Transfer-Encoding", "X-Mailer", "X-Priority" }; /** * MessageListFunctions Constructor * * @param context The Context of the calling XQuery */ public MessageListFunctions( XQueryContext context, FunctionSignature signature ) { super( context, signature ); } /** * evaluate the call to the xquery get-message-list function, * it is really the main entry point of this class * * @param args arguments from the get-message-list() function call * @param contextSequence the Context Sequence to operate on (not used here internally!) * @return A sequence representing the result of the get-message-list() function call * * @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[], org.exist.xquery.value.Sequence) */ public Sequence eval( Sequence[] args, Sequence contextSequence ) throws XPathException { if( isCalledAs( "get-message-list" ) ) { Sequence messageList = getMessageList( args, contextSequence ); return messageList; } else if( isCalledAs( "search-message-list" ) ) { Sequence searchMessageList = searchMessageList( args, contextSequence ); return searchMessageList; } else if( isCalledAs( "get-message-list-as-xml" ) ) { Sequence messageListAsXML = getMessageListAsXML( args, contextSequence ); return messageListAsXML; } else if( isCalledAs( "close-message-list" ) ) { Sequence closeMessageList = closeMessageList( args, contextSequence ); return closeMessageList; } throw( new XPathException(this, "Invalid function name" ) ); } //*************************************************************************** //* //* Function Implementation Methods //* //***************************************************************************/ private Sequence getMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException { Message[] msgList; // was a folder handle specified? if( args[0].isEmpty() ) { throw( new XPathException(this, "Folder handle not specified" ) ); } // get the Folder long folderHandle = ((IntegerValue)args[0].itemAt(0)).getLong(); Folder folder= MailModule.retrieveFolder( context, folderHandle ); if( folder == null ) { throw( new XPathException(this, "Invalid Folder handle specified" ) ); } try { msgList = folder.getMessages(); prefetchMessages( folder, msgList ); } catch( MessagingException me ) { throw( new XPathException(this, "Failed to get mail list", me ) ); } // save the message list and return the handle of the message list return( new IntegerValue( MailModule.storeMessageList( context, msgList, folderHandle ) ) ); } private Sequence searchMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException { Message[] msgList; // was a folder handle specified? if( args[0].isEmpty() || args[1].isEmpty() ) { throw( new XPathException(this, "Folder handle or Search Terms not specified" ) ); } // get the Folder long folderHandle = ((IntegerValue)args[0].itemAt(0)).getLong(); Folder folder= MailModule.retrieveFolder( context, folderHandle ); if( folder == null ) { throw( new XPathException(this, "Invalid Folder handle specified" ) ); } Node searchTermsXML = ( (NodeValue)args[1].itemAt( 0 ) ).getNode(); try { msgList = folder.search( parseSearchTerms( searchTermsXML ) ); prefetchMessages( folder, msgList ); } catch( MessagingException me ) { throw( new XPathException(this, "Failed to get mail list", me ) ); } // save the message list and return the handle of the message list return( new IntegerValue( MailModule.storeMessageList( context, msgList, folderHandle ) ) ); } private void prefetchMessages( Folder folder, Message[] msgList ) throws MessagingException { // Prefetch all the key information and headers FetchProfile fp = new FetchProfile(); fp.add( FetchProfile.Item.ENVELOPE ); for( int i = 0; i < PREFETCH_HEADERS.length; i++ ) { fp.add( PREFETCH_HEADERS[i] ); } folder.fetch( msgList, fp ); } private Sequence getMessageListAsXML( Sequence[] args, Sequence contextSequence ) throws XPathException { Message[] msgList; Sequence ret = Sequence.EMPTY_SEQUENCE; // was a msgList handle specified? if( args[0].isEmpty() ) { throw( new XPathException(this, "Message List handle not specified" ) ); } // get the MessageList long msgListHandle = ((IntegerValue)args[0].itemAt(0)).getLong(); msgList = MailModule.retrieveMessageList( context, msgListHandle ); if( msgList == null ) { throw( new XPathException(this, "Invalid Message List handle specified" ) ); } if( msgList.length > 0 ) { boolean includeHeaders = args[1].effectiveBooleanValue(); MemTreeBuilder builder = context.getDocumentBuilder(); builder.startDocument(); builder.startElement( new QName( "messages", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "count", null, null ), String.valueOf( msgList.length ) ); try { for( int i = 0; i < msgList.length; i++ ) { Message message = msgList[ i ]; builder.startElement( new QName( "message", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "number", null, null ), String.valueOf( i ) ); // Sent Date if( message.getSentDate() != null ) { builder.startElement( new QName( "sent", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.characters( formatDate( message.getSentDate() ) ); builder.endElement(); } // Received Date if( message.getReceivedDate() != null ) { builder.startElement( new QName( "received", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.characters( formatDate( message.getReceivedDate() ) ); builder.endElement(); } // From if( message.getFrom() != null ) { builder.startElement( new QName( "from", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.characters( message.getFrom()[0].toString() ); builder.endElement(); } // Recipients builder.startElement( new QName( "recipients", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); // To Recipients Address[] to = message.getRecipients( Message.RecipientType.TO ); if( to != null ) { for( int j = 0; j < to.length; j++ ) { builder.startElement( new QName( "recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "to" ); builder.characters( to[j].toString() ); builder.endElement(); } } // cc Recipients Address[] cc = message.getRecipients( Message.RecipientType.CC ); if( cc != null ) { for( int j = 0; j < cc.length; j++ ) { builder.startElement( new QName( "recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "cc" ); builder.characters( cc[j].toString() ); builder.endElement(); } } // bcc Recipients Address[] bcc = message.getRecipients( Message.RecipientType.BCC ); if( bcc != null ) { for( int j = 0; j < cc.length; j++ ) { builder.startElement( new QName( "recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "bcc" ); builder.characters( bcc[j].toString() ); builder.endElement(); } } builder.endElement(); // Flags Flags flags = message.getFlags(); Flags.Flag[] sf = flags.getSystemFlags(); String[] uf = flags.getUserFlags(); if( sf.length > 0 || uf.length > 0 ) { builder.startElement( new QName( "flags", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); // System Flags for( int f = 0; f < sf.length; f++ ) { if( sf[f] == Flags.Flag.ANSWERED ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "answered" ); builder.endElement(); } else if( sf[f] == Flags.Flag.DELETED ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "deleted" ); builder.endElement(); } else if( sf[f] == Flags.Flag.DRAFT ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "draft" ); builder.endElement(); } else if( sf[f] == Flags.Flag.FLAGGED ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "flagged" ); builder.endElement(); } else if( sf[f] == Flags.Flag.RECENT ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "recent" ); builder.endElement(); } else if( sf[f] == Flags.Flag.SEEN ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "seen" ); builder.endElement(); } } // User Flags for( int f = 0; f < uf.length; f++ ) { builder.startElement( new QName( "flag", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "type", null, null ), "user" ); builder.addAttribute( new QName( "value", null, null ), uf[ f ] ); builder.endElement(); } builder.endElement(); } // Headers if( includeHeaders ) { Enumeration headers = message.getAllHeaders(); if( headers.hasMoreElements() ) { builder.startElement( new QName( "headers", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); while( headers.hasMoreElements() ) { Header header = (Header)headers.nextElement(); builder.startElement( new QName( "header", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.addAttribute( new QName( "name", null, null ), header.getName() ); builder.addAttribute( new QName( "value", null, null ), header.getValue() ); builder.endElement(); } builder.endElement(); } } // Subject builder.startElement( new QName( "subject", MailModule.NAMESPACE_URI, MailModule.PREFIX ), null ); builder.characters( message.getSubject() ); builder.endElement(); builder.endElement(); } } catch( MessagingException me ) { throw( new XPathException(this, "Failed to retrieve messages from list", me ) ); } builder.endElement(); ret = (NodeValue)builder.getDocument().getDocumentElement(); } return( ret ); } private String formatDate( Date date ) { String formatted = ""; SimpleDateFormat sdf = new SimpleDateFormat( DATE_FORMAT ); String temp = sdf.format( date ); formatted = temp.substring( 0, temp.length() - 2 ) + ":" + temp.substring( temp.length() - 2 ); return( formatted ); } private Sequence closeMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException { // was a msgList handle specified? if( args[0].isEmpty() ) { throw( new XPathException(this, "Message List handle not specified" ) ); } // get the msgList long msgListHandle = ((IntegerValue)args[0].itemAt(0)).getLong(); MailModule.removeMessageList( context, msgListHandle ); return( Sequence.EMPTY_SEQUENCE ); } //*************************************************************************** //* //* Search Term Methods //* //***************************************************************************/ private SearchTerm parseSearchTerms( Node terms ) throws XPathException { SearchTerm st = null; if( terms.getNodeType() == Node.ELEMENT_NODE && terms.getLocalName().equalsIgnoreCase( "searchTerm" ) ) { String type = ((Element)terms).getAttribute( "type" ); if( type != null ) { if( type.equalsIgnoreCase( "not" ) ) { st = new NotTerm( parseChildSearchTerm( terms ) ); } else if( type.equalsIgnoreCase( "and" ) ) { st = new AndTerm( parseChildSearchTerms( terms ) ); } else if( type.equalsIgnoreCase( "or" ) ) { st = new OrTerm( parseChildSearchTerms( terms ) ); } else if( type.equalsIgnoreCase( "from" ) ) { st = parseFromTerm( terms ); } else if( type.equalsIgnoreCase( "subject" ) ) { st = parseSubjectTerm( terms ); } else if( type.equalsIgnoreCase( "body" ) ) { st = parseBodyTerm( terms ); } else if( type.equalsIgnoreCase( "to" ) || type.equalsIgnoreCase( "recipient" ) ) { st = parseRecipientTerm( terms ); } else if( type.equalsIgnoreCase( "header" ) ) { st = parseHeaderTerm( terms ); } else if( type.equalsIgnoreCase( "flag" ) ) { st = parseFlagTerm( terms ); } else if( type.equalsIgnoreCase( "sent" ) ) { st = parseSentDateTerm( terms ); } else if( type.equalsIgnoreCase( "received" ) ) { st = parseReceivedDateTerm( terms ); } else { throw( new XPathException(this, "Invalid Search Term type specified: " + type ) ); } } else { throw( new XPathException(this, "Invalid Search Term type specified: null" ) ); } } if( st == null ) { throw( new XPathException(this, "Invalid Search Terms specified" ) ); } return( st ); } private SearchTerm parseChildSearchTerm( Node terms ) throws XPathException { // Parent only allows a single child search term SearchTerm st = null; NodeList children = terms.getChildNodes(); if( children.getLength() == 1 ) { Node child = children.item( 0 ); st = parseSearchTerms( child ); } else { throw( new XPathException(this, "Only one child term is allowed for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm[] parseChildSearchTerms( Node terms ) throws XPathException { // Parent allows multiple child search terms ArrayList<SearchTerm> st = new ArrayList<SearchTerm>(); NodeList children = terms.getChildNodes(); if( children.getLength() > 0 ) { for( int i = 0; i < children.getLength(); i++ ) { Node child = children.item( i ); st.add( parseSearchTerms( child ) ); } } else { throw( new XPathException(this, "At least one child term is required for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( (SearchTerm[])st.toArray( new SearchTerm[] {} ) ); } private SearchTerm parseFromTerm( Node terms ) throws XPathException { SearchTerm st = null; String pattern = ((Element)terms).getAttribute( "pattern" ); if( pattern != null && pattern.length() > 0 ) { st = new FromStringTerm( pattern ); } else { throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseSubjectTerm( Node terms ) throws XPathException { SearchTerm st = null; String pattern = ((Element)terms).getAttribute( "pattern" ); if( pattern != null && pattern.length() > 0 ) { st = new SubjectTerm( pattern ); } else { throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseBodyTerm( Node terms ) throws XPathException { SearchTerm st = null; String pattern = ((Element)terms).getAttribute( "pattern" ); if( pattern != null && pattern.length() > 0 ) { st = new BodyTerm( pattern ); } else { throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseRecipientTerm( Node terms ) throws XPathException { SearchTerm st = null; String pattern = ((Element)terms).getAttribute( "pattern" ); String type = ((Element)terms).getAttribute( "recipientType" ); if( type == null || type.length() == 0 ) { throw( new XPathException(this, "recipientType not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } if( pattern != null && pattern.length() > 0 ) { Message.RecipientType rtype = null; if( type.equalsIgnoreCase( "to" ) ) { rtype = Message.RecipientType.TO; } else if( type.equalsIgnoreCase( "cc" ) ) { rtype = Message.RecipientType.CC; } else if( type.equalsIgnoreCase( "bcc" ) ) { rtype = Message.RecipientType.BCC; } else { throw( new XPathException(this, "Invalid recipientType: " + type + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } st = new RecipientStringTerm( rtype, pattern ); } else { throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseHeaderTerm( Node terms ) throws XPathException { SearchTerm st = null; String pattern = ((Element)terms).getAttribute( "pattern" ); String name = ((Element)terms).getAttribute( "name" ); if( name == null || name.length() == 0 ) { throw( new XPathException(this, "name not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } if( pattern != null && pattern.length() > 0 ) { st = new HeaderTerm( name, pattern ); } else { throw( new XPathException(this, "pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseFlagTerm( Node terms ) throws XPathException { SearchTerm st = null; String flag = ((Element)terms).getAttribute( "flag" ); String value = ((Element)terms).getAttribute( "value" ); if( value == null || value.length() == 0 ) { throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } if( flag != null && flag.length() > 0 ) { Flags flags = null; if( flag.equalsIgnoreCase( "answered" ) ) { flags = new Flags( Flags.Flag.ANSWERED ); } else if( flag.equalsIgnoreCase( "deleted" ) ) { flags = new Flags( Flags.Flag.DELETED ); } else if( flag.equalsIgnoreCase( "draft" ) ) { flags = new Flags( Flags.Flag.DRAFT ); } else if( flag.equalsIgnoreCase( "recent" ) ) { flags = new Flags( Flags.Flag.RECENT ); } else if( flag.equalsIgnoreCase( "seen" ) ) { flags = new Flags( Flags.Flag.SEEN ); } else { throw( new XPathException(this, "Invalid flag: " + flag + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } st = new FlagTerm( flags, value.equalsIgnoreCase( "true" ) ); } else { throw( new XPathException(this, "flag attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseSentDateTerm( Node terms ) throws XPathException { SearchTerm st = null; String value = ((Element)terms).getAttribute( "date" ); String format = ((Element)terms).getAttribute( "format" ); if( value == null || value.length() == 0 ) { throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } if( format == null || format.length() == 0 ) { throw( new XPathException(this, "format not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } int cp = parseComparisonAttribute( terms ); try { SimpleDateFormat sdf = new SimpleDateFormat( format ); Date date = sdf.parse( value ); st = new SentDateTerm( cp, date ); } catch( ParseException pe ) { throw( new XPathException(this, "Cannot parse date value: " + value + ", using format: " + format + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private SearchTerm parseReceivedDateTerm( Node terms ) throws XPathException { SearchTerm st = null; String value = ((Element)terms).getAttribute( "date" ); String format = ((Element)terms).getAttribute( "format" ); if( value == null || value.length() == 0 ) { throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } if( format == null || format.length() == 0 ) { throw( new XPathException(this, "format not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } int cp = parseComparisonAttribute( terms ); try { SimpleDateFormat sdf = new SimpleDateFormat( format ); Date date = sdf.parse( value ); st = new ReceivedDateTerm( cp, date ); } catch( ParseException pe ) { throw( new XPathException(this, "Cannot parse date value: " + value + ", using format: " + format + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( st ); } private int parseComparisonAttribute( Node terms ) throws XPathException { int cp = ComparisonTerm.EQ; String comp = ((Element)terms).getAttribute( "comparison" ); if( comp != null && comp.length() > 0 ) { if( comp.equalsIgnoreCase( "eq" ) ) { cp = ComparisonTerm.EQ; } else if( comp.equalsIgnoreCase( "ge" ) ) { cp = ComparisonTerm.GE; } else if( comp.equalsIgnoreCase( "gt" ) ) { cp = ComparisonTerm.GT; } else if( comp.equalsIgnoreCase( "le" ) ) { cp = ComparisonTerm.LE; } else if( comp.equalsIgnoreCase( "lt" ) ) { cp = ComparisonTerm.LT; } else if( comp.equalsIgnoreCase( "ne" ) ) { cp = ComparisonTerm.NE; } else { throw( new XPathException(this, "Invalid comparison: " + comp + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } } else { throw( new XPathException(this, "comparison attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) ); } return( cp ); } }