• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/extensions/modules/mail/src/main/java/org/exist/xquery/modules/mail/MessageListFunctions.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.xquery.modules.mail;
50

51

52
import java.text.ParseException;
53
import java.text.SimpleDateFormat;
54
import java.util.ArrayList;
55
import java.util.Date;
56
import java.util.Enumeration;
57
import jakarta.mail.Address;
58
import jakarta.mail.FetchProfile;
59
import jakarta.mail.Flags;
60
import jakarta.mail.Folder;
61
import jakarta.mail.Header;
62
import jakarta.mail.Message;
63
import jakarta.mail.MessagingException;
64
import jakarta.mail.search.AndTerm;
65
import jakarta.mail.search.BodyTerm;
66
import jakarta.mail.search.ComparisonTerm;
67
import jakarta.mail.search.FlagTerm;
68
import jakarta.mail.search.FromStringTerm;
69
import jakarta.mail.search.HeaderTerm;
70
import jakarta.mail.search.NotTerm;
71
import jakarta.mail.search.OrTerm;
72
import jakarta.mail.search.ReceivedDateTerm;
73
import jakarta.mail.search.RecipientStringTerm;
74
import jakarta.mail.search.SearchTerm;
75
import jakarta.mail.search.SentDateTerm;
76
import jakarta.mail.search.SubjectTerm;
77
import org.apache.logging.log4j.LogManager;
78
import org.apache.logging.log4j.Logger;
79
import org.exist.dom.QName;
80
import org.exist.dom.memtree.MemTreeBuilder;
81
import org.exist.xquery.BasicFunction;
82
import org.exist.xquery.Cardinality;
83
import org.exist.xquery.FunctionSignature;
84
import org.exist.xquery.XPathException;
85
import org.exist.xquery.XQueryContext;
86
import org.exist.xquery.value.FunctionParameterSequenceType;
87
import org.exist.xquery.value.FunctionReturnSequenceType;
88
import org.exist.xquery.value.IntegerValue;
89
import org.exist.xquery.value.NodeValue;
90
import org.exist.xquery.value.Sequence;
91
import org.exist.xquery.value.SequenceType;
92
import org.exist.xquery.value.Type;
93
import org.w3c.dom.Element;
94
import org.w3c.dom.Node;
95
import org.w3c.dom.NodeList;
96

97
/**
98
 * eXist Mail Module Extension GetMessageList
99
 * 
100
 * Get a mail store
101
 * 
102
 * @author <a href="mailto:andrzej@chaeron.com">Andrzej Taramina</a>
103
 * @serial 2009-03-12
104
 * @version 1.3
105
 *
106
 * @see org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext, org.exist.xquery.FunctionSignature)
107
 */
108
public class MessageListFunctions extends BasicFunction
109
{
110
        protected static final Logger logger = LogManager.getLogger(MessageListFunctions.class);
×
111

112
        public final static FunctionSignature signatures[] = {
×
113
                new FunctionSignature(
114
                        new QName( "get-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ),
115
                        "Returns a message list of all messages in a folder.",
116
                        new SequenceType[]
117
                        {
118
                                new FunctionParameterSequenceType( "mail-folder-handle", Type.LONG, Cardinality.EXACTLY_ONE, "The mail folder handle retrieved from mail:get-mail-folder()" )
119
                        },
120
                        new FunctionReturnSequenceType( Type.LONG, Cardinality.ZERO_OR_ONE, "an xs:long representing the message list handle." )
121
                        ),
122
                
123
                new FunctionSignature(
124
                        new QName( "search-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ),
125
                        "Searches messages in a folder. " +
126
                        "Search terms are of the form <searchTerm type=\"xxx\">...</searchTerm>.  Valid types include: not, and, or, from, subject, body, recipient, header, flag, sent, received. " +
127
                        "<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. " +
128
                        "<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. " +
129
                        "<searchTerm type=\"recipient\" pattern=\"pat\" recipientType=\"to|cc|bcc\"> requires pattern and recipientType attributes. " +
130
                        "<searchTerm type=\"header\" pattern=\"pat\" name=\"Content-Type\"> requires pattern and name attributes. " +
131
                        "<searchTerm type=\"flag\" flag=\"answered|deleted|draft|recent|seen\" value=\"true|false\"> requires flag and value attributes. " +
132
                        "<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. " +
133
                        "The format string should conform to Java SimpleDateFormat specifications and the date string must conform to the specified format string.",
134
                        new SequenceType[]
135
                        {
136
                                new FunctionParameterSequenceType( "mail-folder-handle", Type.LONG, Cardinality.EXACTLY_ONE, "The mail folder handle retrieved from mail:get-mail-folder()" ),
137
                                new FunctionParameterSequenceType( "search-parameters", Type.ELEMENT, Cardinality.EXACTLY_ONE, "The xml fragment defining the search terms" )
138
                        },
139
                        new FunctionReturnSequenceType( Type.LONG, Cardinality.ZERO_OR_ONE, "an xs:long representing the message list handle." )
140
                        ),
141
                
142
                new FunctionSignature(
143
                        new QName( "get-message-list-as-xml", MailModule.NAMESPACE_URI, MailModule.PREFIX ),
144
                        "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",
145
                        new SequenceType[]
146
                        {
147
                                new FunctionParameterSequenceType( "message-list-handle", Type.LONG, Cardinality.EXACTLY_ONE, "The message list handle retrieved from mail:get-message-list() or mail:search-message-list()" ),
148
                                new FunctionParameterSequenceType( "include-headers", Type.BOOLEAN, Cardinality.EXACTLY_ONE, "A boolean specifying whether to include message headers" )
149
                        },
150
                        new FunctionReturnSequenceType( Type.ELEMENT, Cardinality.ZERO_OR_ONE, "the list of all messages in a folder as XML" )
151
                        ),
152
                
153
                new FunctionSignature(
154
                        new QName( "close-message-list", MailModule.NAMESPACE_URI, MailModule.PREFIX ),
155
                        "Closes a message list.",
156
                        new SequenceType[]
157
                        {
158
                                new FunctionParameterSequenceType( "message-list-handle", Type.LONG, Cardinality.EXACTLY_ONE, "The message list handle retrieved from mail:get-message-list() or mail:search-message-list()" )
159
                        },
160
                        new SequenceType( Type.ITEM, Cardinality.EMPTY_SEQUENCE )
161
                        )
162
        };
163
        
164
        private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
165
        
166
        private static final String PREFETCH_HEADERS[] = {
×
167
                "Return-Path",
168
                "Delivered-To",
169
                "Received",
170
                "Date",
171
                "From",
172
                "To",
173
                "Message-ID",
174
                "Subject",
175
                "MIME-Version",
176
                "Content-Type",
177
                "Content-Transfer-Encoding",
178
                "X-Mailer",
179
                "X-Priority"
180
        };
181

182
        public MessageListFunctions( XQueryContext context, FunctionSignature signature )
183
        {
184
                super( context, signature );
×
185
    }
×
186

187
        @Override
188
        public Sequence eval( Sequence[] args, Sequence contextSequence ) throws XPathException
189
        {
190
                if( isCalledAs( "get-message-list" ) ) {
×
191
            return getMessageList( args, contextSequence );
×
192
                } else if( isCalledAs( "search-message-list" ) ) {
×
193
            return searchMessageList( args, contextSequence );
×
194
                } else if( isCalledAs( "get-message-list-as-xml" ) ) {
×
195
            return getMessageListAsXML( args, contextSequence );
×
196
                } else if( isCalledAs( "close-message-list" ) ) {
×
197
            return closeMessageList( args, contextSequence );
×
198
                } 
199
                        
200
                throw( new XPathException(this, "Invalid function name" ) );        
×
201
        }
202
        
203
        
204
        //***************************************************************************
205
        //*
206
        //*    Function Implementation Methods
207
        //*
208
        //***************************************************************************/
209
        
210
        private Sequence getMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException
211
        {
212
                Message[] msgList;
213
                
214
                // was a folder handle specified?
215
                if( args[0].isEmpty() ) {
×
216
                        throw( new XPathException(this, "Folder handle not specified" ) );
×
217
                }
218

219
                // get the Folder
220
                long folderHandle = ((IntegerValue)args[0].itemAt(0)).getLong();
×
221
                Folder folder= MailModule.retrieveFolder( context, folderHandle );
×
222
                if( folder == null ) {
×
223
                        throw( new XPathException(this, "Invalid Folder handle specified" ) );
×
224
                }
225
                
226
                try {
227
                        msgList = folder.getMessages();
×
228
                        prefetchMessages( folder, msgList );
×
229
                }
230
                catch( MessagingException me ) {
×
231
                        throw( new XPathException(this, "Failed to get mail list", me ) );
×
232
                }
×
233
                
234
                // save the message list and return the handle of the message list
235
                        
236
                return( new IntegerValue( this, MailModule.storeMessageList( context, msgList, folderHandle ), Type.LONG ) );
×
237
        }
238

239
        private Sequence searchMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException
240
        {
241
                Message[] msgList;
242
                
243
                // was a folder handle specified?
244
                if( args[0].isEmpty() || args[1].isEmpty() ) {
×
245
                        throw( new XPathException(this, "Folder handle or Search Terms not specified" ) );
×
246
                }
247

248
                // get the Folder
249
                long folderHandle = ((IntegerValue)args[0].itemAt(0)).getLong();
×
250
                Folder folder= MailModule.retrieveFolder( context, folderHandle );
×
251
                if( folder == null ) {
×
252
                        throw( new XPathException(this, "Invalid Folder handle specified" ) );
×
253
                }
254
                
255
                Node searchTermsXML = ( (NodeValue)args[1].itemAt( 0 ) ).getNode();
×
256
                
257
                try {
258
                        msgList = folder.search( parseSearchTerms( searchTermsXML ) );
×
259
                        
260
                        prefetchMessages( folder, msgList );
×
261
                }
262
                catch( MessagingException me ) {
×
263
                        throw( new XPathException(this, "Failed to get mail list", me ) );
×
264
                }
×
265
                
266
                // save the message list and return the handle of the message list
267
                        
268
                return( new IntegerValue( this, MailModule.storeMessageList( context, msgList, folderHandle ), Type.LONG ) );
×
269
        }
270

271
        private void prefetchMessages( Folder folder, Message[] msgList ) throws MessagingException
272
        {
273
                // Prefetch all the key information and headers
274
                
275
                FetchProfile fp = new FetchProfile();
×
276
                fp.add( FetchProfile.Item.ENVELOPE );
×
277
                
278
        for (String PREFETCH_HEADER : PREFETCH_HEADERS) {
×
279
            fp.add(PREFETCH_HEADER);
×
280
        }
281
                folder.fetch( msgList, fp );
×
282
        }
×
283

284
        private Sequence getMessageListAsXML(Sequence[] args, Sequence contextSequence) throws XPathException {
285
                Message[] msgList;
286
                Sequence ret = Sequence.EMPTY_SEQUENCE;
×
287

288
                // was a msgList handle specified?
289
                if (args[0].isEmpty()) {
×
290
                        throw (new XPathException(this, "Message List handle not specified"));
×
291
                }
292

293
                // get the MessageList
294
                long msgListHandle = ((IntegerValue) args[0].itemAt(0)).getLong();
×
295
                msgList = MailModule.retrieveMessageList(context, msgListHandle);
×
296
                if (msgList == null) {
×
297
                        throw (new XPathException(this, "Invalid Message List handle specified"));
×
298
                }
299

300
                if (msgList.length > 0) {
×
301

302
                        boolean includeHeaders = args[1].effectiveBooleanValue();
×
303

304
                        context.pushDocumentContext();
×
305
                        try {
306
                                MemTreeBuilder builder = context.getDocumentBuilder();
×
307

308
                                builder.startDocument();
×
309
                                builder.startElement(new QName("messages", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
310
                                builder.addAttribute(new QName("count", null, null), String.valueOf(msgList.length));
×
311

312
                                try {
313
                                        for (Message message : msgList) {
×
314
                                                builder.startElement(new QName("message", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
315

316
                                                builder.addAttribute(new QName("number", null, null), String.valueOf(message.getMessageNumber()));
×
317

318
                                                // Sent Date
319
                                                if (message.getSentDate() != null) {
×
320
                                                        builder.startElement(new QName("sent", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
321
                                                        builder.characters(formatDate(message.getSentDate()));
×
322
                                                        builder.endElement();
×
323
                                                }
324

325
                                                // Received Date
326
                                                if (message.getReceivedDate() != null) {
×
327
                                                        builder.startElement(new QName("received", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
328
                                                        builder.characters(formatDate(message.getReceivedDate()));
×
329
                                                        builder.endElement();
×
330
                                                }
331

332
                                                // From
333
                                                if (message.getFrom() != null) {
×
334
                                                        builder.startElement(new QName("from", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
335
                                                        builder.characters(message.getFrom()[0].toString());
×
336
                                                        builder.endElement();
×
337
                                                }
338

339
                                                // Recipients
340
                                                builder.startElement(new QName("recipients", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
341
                                                // To Recipients
342
                                                Address[] toAddresses = message.getRecipients(Message.RecipientType.TO);
×
343
                                                if (toAddresses != null) {
×
344
                                                        for (Address to : toAddresses) {
×
345
                                                                builder.startElement(new QName("recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
346
                                                                builder.addAttribute(new QName("type", null, null), "to");
×
347
                                                                builder.characters(to.toString());
×
348
                                                                builder.endElement();
×
349
                                                        }
350
                                                }
351

352
                                                // cc Recipients
353
                                                Address[] ccAddresses = message.getRecipients(Message.RecipientType.CC);
×
354
                                                if (ccAddresses != null) {
×
355
                                                        for (Address ccAddress : ccAddresses) {
×
356
                                                                builder.startElement(new QName("recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
357
                                                                builder.addAttribute(new QName("type", null, null), "cc");
×
358
                                                                builder.characters(ccAddress.toString());
×
359
                                                                builder.endElement();
×
360
                                                        }
361
                                                }
362

363
                                                // bcc Recipients
364
                                                Address[] bccAddresses = message.getRecipients(Message.RecipientType.BCC);
×
365
                                                if (bccAddresses != null) {
×
366
                                                        for (Address bccAddress : bccAddresses) {
×
367
                                                                builder.startElement(new QName("recipient", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
368
                                                                builder.addAttribute(new QName("type", null, null), "bcc");
×
369
                                                                builder.characters(bccAddress.toString());
×
370
                                                                builder.endElement();
×
371
                                                        }
372
                                                }
373
                                                builder.endElement();
×
374

375
                                                // Flags
376

377
                                                Flags flags = message.getFlags();
×
378
                                                Flags.Flag[] systemFlags = flags.getSystemFlags();
×
379
                                                String[] userFlags = flags.getUserFlags();
×
380

381
                                                if (systemFlags.length > 0 || userFlags.length > 0) {
×
382
                                                        builder.startElement(new QName("flags", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
383

384
                                                        for (Flags.Flag systemFlag : systemFlags) {
×
385
                                                                if (systemFlag == Flags.Flag.ANSWERED) {
×
386
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
387
                                                                        builder.addAttribute(new QName("type", null, null), "answered");
×
388
                                                                        builder.endElement();
×
389
                                                                } else if (systemFlag == Flags.Flag.DELETED) {
×
390
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
391
                                                                        builder.addAttribute(new QName("type", null, null), "deleted");
×
392
                                                                        builder.endElement();
×
393
                                                                } else if (systemFlag == Flags.Flag.DRAFT) {
×
394
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
395
                                                                        builder.addAttribute(new QName("type", null, null), "draft");
×
396
                                                                        builder.endElement();
×
397
                                                                } else if (systemFlag == Flags.Flag.FLAGGED) {
×
398
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
399
                                                                        builder.addAttribute(new QName("type", null, null), "flagged");
×
400
                                                                        builder.endElement();
×
401
                                                                } else if (systemFlag == Flags.Flag.RECENT) {
×
402
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
403
                                                                        builder.addAttribute(new QName("type", null, null), "recent");
×
404
                                                                        builder.endElement();
×
405
                                                                } else if (systemFlag == Flags.Flag.SEEN) {
×
406
                                                                        builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
407
                                                                        builder.addAttribute(new QName("type", null, null), "seen");
×
408
                                                                        builder.endElement();
×
409
                                                                }
410
                                                        }
411

412
                                                        for (String userFlag : userFlags) {
×
413
                                                                builder.startElement(new QName("flag", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
414
                                                                builder.addAttribute(new QName("type", null, null), "user");
×
415
                                                                builder.addAttribute(new QName("value", null, null), userFlag);
×
416
                                                                builder.endElement();
×
417
                                                        }
418

419
                                                        builder.endElement();
×
420
                                                }
421

422
                                                // Headers
423

424
                                                if (includeHeaders) {
×
425
                                                        Enumeration headers = message.getAllHeaders();
×
426

427
                                                        if (headers.hasMoreElements()) {
×
428
                                                                builder.startElement(new QName("headers", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
429

430
                                                                while (headers.hasMoreElements()) {
×
431
                                                                        Header header = (Header) headers.nextElement();
×
432

433
                                                                        builder.startElement(new QName("header", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
434
                                                                        builder.addAttribute(new QName("name", null, null), header.getName());
×
435
                                                                        builder.addAttribute(new QName("value", null, null), header.getValue());
×
436
                                                                        builder.endElement();
×
437
                                                                }
×
438

439
                                                                builder.endElement();
×
440
                                                        }
441
                                                }
442

443
                                                // Subject
444
                                                builder.startElement(new QName("subject", MailModule.NAMESPACE_URI, MailModule.PREFIX), null);
×
445
                                                builder.characters(message.getSubject());
×
446
                                                builder.endElement();
×
447

448
                                                builder.endElement();
×
449
                                        }
450
                                } catch (MessagingException me) {
×
451
                                        throw (new XPathException(this, "Failed to retrieve messages from list", me));
×
452
                                }
×
453

454
                                builder.endElement();
×
455

456
                                ret = (NodeValue) builder.getDocument().getDocumentElement();
×
457
                        } finally {
458
                                context.popDocumentContext();
×
459

460
                        }
461
                }
462

463
                return (ret);
×
464
        }
465

466
        private String formatDate( Date date ) 
467
        {
468
                String formatted = "";
×
469
                
470
                SimpleDateFormat sdf = new SimpleDateFormat( DATE_FORMAT );
×
471
                
472
                String temp = sdf.format( date );
×
473
                
474
                formatted = temp.substring( 0, temp.length() - 2 ) + ":" + temp.substring( temp.length() - 2 );
×
475
                
476
                return( formatted );
×
477
        }
478

479
        private Sequence closeMessageList( Sequence[] args, Sequence contextSequence ) throws XPathException
480
        {
481
                // was a msgList handle specified?
482
                if( args[0].isEmpty() ) {
×
483
                        throw( new XPathException(this, "Message List handle not specified" ) );
×
484
                }
485
                
486
                // get the msgList
487
                long msgListHandle = ((IntegerValue)args[0].itemAt(0)).getLong();
×
488
        
489
                MailModule.removeMessageList( context, msgListHandle );
×
490
                        
491
                return( Sequence.EMPTY_SEQUENCE );
×
492
        }
493
        
494
        
495
        //***************************************************************************
496
        //*
497
        //*    Search Term Methods
498
        //*
499
        //***************************************************************************/
500
        
501
        private SearchTerm parseSearchTerms( Node terms ) throws XPathException
502
        {
503
                SearchTerm        st = null;
×
504
                
505
                if( terms.getNodeType() == Node.ELEMENT_NODE && terms.getLocalName().equalsIgnoreCase( "searchTerm" ) ) {
×
506
                        String type  = ((Element)terms).getAttribute( "type" );
×
507

508
                                if( type.equalsIgnoreCase( "not" ) ) {
×
509
                                        st = new NotTerm( parseChildSearchTerm( terms ) );
×
510
                                } else if( type.equalsIgnoreCase( "and" ) ) {
×
511
                                        st = new AndTerm( parseChildSearchTerms( terms ) );
×
512
                                } else if( type.equalsIgnoreCase( "or" ) ) {
×
513
                                        st = new OrTerm( parseChildSearchTerms( terms ) );
×
514
                                } else if( type.equalsIgnoreCase( "from" ) ) {
×
515
                                        st = parseFromTerm( terms );
×
516
                                } else if( type.equalsIgnoreCase( "subject" ) ) {
×
517
                                        st = parseSubjectTerm( terms );
×
518
                                } else if( type.equalsIgnoreCase( "body" ) ) {
×
519
                                        st = parseBodyTerm( terms );
×
520
                                } else if( type.equalsIgnoreCase( "to" ) || type.equalsIgnoreCase( "recipient" ) ) {
×
521
                                        st = parseRecipientTerm( terms );
×
522
                                } else if( type.equalsIgnoreCase( "header" ) ) {
×
523
                                        st = parseHeaderTerm( terms );
×
524
                                } else if( type.equalsIgnoreCase( "flag" ) ) {
×
525
                                        st = parseFlagTerm( terms );
×
526
                                } else if( type.equalsIgnoreCase( "sent" ) ) {
×
527
                                        st = parseSentDateTerm( terms );
×
528
                                } else if( type.equalsIgnoreCase( "received" ) ) {
×
529
                                        st = parseReceivedDateTerm( terms );
×
530
                                } else {
531
                                        throw( new XPathException(this, "Invalid Search Term type specified: " + type ) );
×
532
                                }
533
                } 
534
                
535
                if( st == null ) {
×
536
                        throw( new XPathException(this, "Invalid Search Terms specified" ) );
×
537
                }
538
                
539
                return( st );
×
540
        }
541

542
        private SearchTerm parseChildSearchTerm( Node terms ) throws XPathException
543
        {
544
                // Parent only allows a single child search term
545
                
546
                SearchTerm        st = null;
×
547
                
548
                NodeList children = terms.getChildNodes();
×
549
                
550
                if( children.getLength() == 1 ) {
×
551
                        Node child = children.item( 0 );
×
552
                        
553
                        st = parseSearchTerms( child );
×
554
                } else {
×
555
                        throw( new XPathException(this, "Only one child term is allowed for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
556
                }
557
                
558
                return( st );
×
559
        }
560

561
        private SearchTerm[] parseChildSearchTerms( Node terms ) throws XPathException
562
        {
563
                // Parent allows multiple child search terms
564
                
565
                ArrayList<SearchTerm> st = new ArrayList<>();
×
566
                
567
                NodeList children = terms.getChildNodes();
×
568
                
569
                if( children.getLength() > 0 ) {
×
570
                        for( int i = 0; i < children.getLength(); i++ ) {
×
571
                                Node child = children.item( i );
×
572
                                
573
                                st.add( parseSearchTerms( child ) );
×
574
                        }
575
                } else {
576
                        throw( new XPathException(this, "At least one child term is required for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
577
                }
578
                
579
                return st.toArray(new SearchTerm[0]);
×
580
        }
581

582
        private SearchTerm parseFromTerm( Node terms ) throws XPathException
583
        {
584
                SearchTerm        st = null;
×
585
                
586
                String pattern  = ((Element)terms).getAttribute( "pattern" );
×
587
                
588
                if(!pattern.isEmpty()) {
×
589
                        st = new FromStringTerm( pattern );
×
590
                } else {
591
                        throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
592
                }
593
                
594
                return( st );
×
595
        }
596

597
        private SearchTerm parseSubjectTerm( Node terms ) throws XPathException
598
        {
599
                SearchTerm        st = null;
×
600
                
601
                String pattern  = ((Element)terms).getAttribute( "pattern" );
×
602
                
603
                if(!pattern.isEmpty()) {
×
604
                        st = new SubjectTerm( pattern );
×
605
                } else {
606
                        throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
607
                }
608
                
609
                return( st );
×
610
        }
611

612
        private SearchTerm parseBodyTerm( Node terms ) throws XPathException
613
        {
614
                SearchTerm        st = null;
×
615
                
616
                String pattern  = ((Element)terms).getAttribute( "pattern" );
×
617
                
618
                if(!pattern.isEmpty()) {
×
619
                        st = new BodyTerm( pattern );
×
620
                } else {
621
                        throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
622
                }
623
                
624
                return( st );
×
625
        }
626

627
        private SearchTerm parseRecipientTerm( Node terms ) throws XPathException
628
        {
629
                SearchTerm        st = null;
×
630
                
631
                String pattern  = ((Element)terms).getAttribute( "pattern" );
×
632
                String type     = ((Element)terms).getAttribute( "recipientType" );
×
633
                
634
                if( type.isEmpty() ) {
×
635
                        throw( new XPathException(this, "recipientType not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
636
                }
637
                
638
                if(!pattern.isEmpty()) {
×
639
                        Message.RecipientType rtype = null;
×
640
                        
641
                        if( type.equalsIgnoreCase( "to" ) ) {
×
642
                                rtype = Message.RecipientType.TO;
×
643
                        } else if( type.equalsIgnoreCase( "cc" ) ) {
×
644
                                rtype = Message.RecipientType.CC;
×
645
                        } else if( type.equalsIgnoreCase( "bcc" ) ) {
×
646
                                rtype = Message.RecipientType.BCC;
×
647
                        } else {
648
                                throw( new XPathException(this, "Invalid recipientType: " + type + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
649
                        }
650
                        
651
                        st = new RecipientStringTerm( rtype, pattern );
×
652
                } else {
×
653
                        throw( new XPathException(this, "Pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
654
                }
655
                
656
                return( st );
×
657
        }
658

659
        private SearchTerm parseHeaderTerm( Node terms ) throws XPathException
660
        {
661
                SearchTerm        st = null;
×
662
                
663
                String pattern  = ((Element)terms).getAttribute( "pattern" );
×
664
                String name     = ((Element)terms).getAttribute( "name" );
×
665
                
666
                if( name.isEmpty() ) {
×
667
                        throw( new XPathException(this, "name not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
668
                }
669
                
670
                if(!pattern.isEmpty()) {
×
671
                        st = new HeaderTerm( name, pattern );
×
672
                } else {
673
                        throw( new XPathException(this, "pattern attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
674
                }
675
                
676
                return( st );
×
677
        }
678

679
        private SearchTerm parseFlagTerm( Node terms ) throws XPathException
680
        {
681
                SearchTerm        st = null;
×
682
                
683
                String flag  = ((Element)terms).getAttribute( "flag" );
×
684
                String value = ((Element)terms).getAttribute( "value" );
×
685
                
686
                if( value.isEmpty() ) {
×
687
                        throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
688
                }
689
                
690
                if( !flag.isEmpty()) {
×
691
                        Flags flags = null;
×
692
                        
693
                        if( flag.equalsIgnoreCase( "answered" ) ) {
×
694
                                flags = new Flags( Flags.Flag.ANSWERED );
×
695
                        } else if( flag.equalsIgnoreCase( "deleted" ) ) {
×
696
                                flags = new Flags( Flags.Flag.DELETED );
×
697
                        } else if( flag.equalsIgnoreCase( "draft" ) ) {
×
698
                                flags = new Flags( Flags.Flag.DRAFT );
×
699
                        } else if( flag.equalsIgnoreCase( "recent" ) ) {
×
700
                                flags = new Flags( Flags.Flag.RECENT );
×
701
                        } else if( flag.equalsIgnoreCase( "seen" ) ) {
×
702
                                flags = new Flags( Flags.Flag.SEEN );
×
703
                        } else {
704
                                throw( new XPathException(this, "Invalid flag: " + flag + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
705
                        }
706
                                
707
                        st = new FlagTerm( flags, value.equalsIgnoreCase( "true" ) );
×
708
                } else {
×
709
                        throw( new XPathException(this, "flag attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
710
                }
711
                
712
                return( st );
×
713
        }
714

715
        private SearchTerm parseSentDateTerm( Node terms ) throws XPathException
716
        {
717
                SearchTerm        st = null;
×
718
                
719
                String value = ((Element)terms).getAttribute( "date" );
×
720
                String format = ((Element)terms).getAttribute( "format" );
×
721
                
722
                if( value.isEmpty() ) {
×
723
                        throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
724
                }
725
                
726
                if( format.isEmpty() ) {
×
727
                        throw( new XPathException(this, "format not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
728
                }
729
                
730
                int  cp = parseComparisonAttribute( terms );
×
731
                
732
                try {
733
                        SimpleDateFormat sdf = new SimpleDateFormat( format );
×
734
                        
735
                        Date date = sdf.parse( value );
×
736
                        
737
                        st = new SentDateTerm( cp, date );
×
738
                }
739
                catch( ParseException pe ) {
×
740
                        throw( new XPathException(this, "Cannot parse date value: " + value + ", using format: " + format + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
741
                }
×
742
                
743
                return( st );
×
744
        }
745

746
        private SearchTerm parseReceivedDateTerm( Node terms ) throws XPathException
747
        {
748
                SearchTerm        st = null;
×
749
                
750
                String value = ((Element)terms).getAttribute( "date" );
×
751
                String format = ((Element)terms).getAttribute( "format" );
×
752
                
753
                if( value.isEmpty() ) {
×
754
                        throw( new XPathException(this, "value not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
755
                }
756
                
757
                if( format.isEmpty() ) {
×
758
                        throw( new XPathException(this, "format not specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
759
                }
760
                
761
                int  cp = parseComparisonAttribute( terms );
×
762
                
763
                try {
764
                        SimpleDateFormat sdf = new SimpleDateFormat( format );
×
765
                        
766
                        Date date = sdf.parse( value );
×
767
                        
768
                        st = new ReceivedDateTerm( cp, date );
×
769
                }
770
                catch( ParseException pe ) {
×
771
                        throw( new XPathException(this, "Cannot parse date value: " + value + ", using format: " + format + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
772
                }
×
773
                
774
                return( st );
×
775
        }
776

777
        private int parseComparisonAttribute( Node terms ) throws XPathException
778
        {
779
                int  cp = ComparisonTerm.EQ;
×
780
                
781
                String comp  = ((Element)terms).getAttribute( "comparison" );
×
782
                
783
                if( !comp.isEmpty()) {
×
784
                        if( comp.equalsIgnoreCase( "eq" ) ) {
×
785
                                cp = ComparisonTerm.EQ;
×
786
                        } else if( comp.equalsIgnoreCase( "ge" ) ) {
×
787
                                cp = ComparisonTerm.GE;
×
788
                        } else if( comp.equalsIgnoreCase( "gt" ) ) {
×
789
                                cp = ComparisonTerm.GT;
×
790
                        } else if( comp.equalsIgnoreCase( "le" ) ) {
×
791
                                cp = ComparisonTerm.LE;
×
792
                        } else if( comp.equalsIgnoreCase( "lt" ) ) {
×
793
                                cp = ComparisonTerm.LT;
×
794
                        } else if( comp.equalsIgnoreCase( "ne" ) ) {
×
795
                                cp = ComparisonTerm.NE;
×
796
                        } else {
797
                                throw( new XPathException(this, "Invalid comparison: " + comp + ", for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
798
                        }
799
                } else {
800
                        throw( new XPathException(this, "comparison attribute must be specified for term with type: " + ((Element)terms).getAttribute( "type" ) ) );
×
801
                }
802
                
803
                return( cp );
×
804
        }
805
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc