**** Merged from MCS ****
[mono-project.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / LdapSearchResults.cs
blob1277d0bff29c34d56babb1280349ffcf59f2b460
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc. www.novell.com
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *******************************************************************************/
24 // Novell.Directory.Ldap.LdapSearchResults.cs
26 // Author:
27 // Sunil Kumar (Sunilk@novell.com)
29 // (C) 2003 Novell, Inc (http://www.novell.com)
32 using System;
33 using Novell.Directory.Ldap.Utilclass;
35 namespace Novell.Directory.Ldap
38 /// <summary> An LdapSearchResults object is returned from a synchronous search
39 /// operation. It provides access to all results received during the
40 /// operation (entries and exceptions).
41 ///
42 /// </summary>
43 /// <seealso cref="LdapConnection.Search">
44 /// </seealso>
45 public class LdapSearchResults
47 /// <summary> Returns a count of the items in the search result.
48 ///
49 /// Returns a count of the entries and exceptions remaining in the object.
50 /// If the search was submitted with a batch size greater than zero,
51 /// getCount reports the number of results received so far but not enumerated
52 /// with next(). If batch size equals zero, getCount reports the number of
53 /// items received, since the application thread blocks until all results are
54 /// received.
55 ///
56 /// </summary>
57 /// <returns> The number of items received but not retrieved by the application
58 /// </returns>
59 virtual public int Count
61 get
63 int qCount = queue.MessageAgent.Count;
64 return entryCount - entryIndex + referenceCount - referenceIndex + qCount;
68 /// <summary> Returns the latest server controls returned by the server
69 /// in the context of this search request, or null
70 /// if no server controls were returned.
71 ///
72 /// </summary>
73 /// <returns> The server controls returned with the search request, or null
74 /// if none were returned.
75 /// </returns>
76 virtual public LdapControl[] ResponseControls
78 get
80 return controls;
84 /// <summary> Collects batchSize elements from an LdapSearchQueue message
85 /// queue and places them in a Vector.
86 ///
87 /// If the last message from the server,
88 /// the result message, contains an error, it will be stored in the Vector
89 /// for nextElement to process. (although it does not increment the search
90 /// result count) All search result entries will be placed in the Vector.
91 /// If a null is returned from getResponse(), it is likely that the search
92 /// was abandoned.
93 ///
94 /// </summary>
95 /// <returns> true if all search results have been placed in the vector.
96 /// </returns>
97 private bool BatchOfResults
99 get
101 LdapMessage msg;
103 // <=batchSize so that we can pick up the result-done message
104 for (int i = 0; i < batchSize; )
108 if ((msg = queue.getResponse()) != null)
110 // Only save controls if there are some
111 LdapControl[] ctls = msg.Controls;
112 if (ctls != null)
115 controls = ctls;
118 if (msg is LdapSearchResult)
120 // Search Entry
121 System.Object entry = ((LdapSearchResult) msg).Entry;
122 entries.Add(entry);
123 i++;
124 entryCount++;
126 else if (msg is LdapSearchResultReference)
128 // Search Ref
129 System.String[] refs = ((LdapSearchResultReference) msg).Referrals;
131 if (cons.ReferralFollowing)
133 // referralConn = conn.chaseReferral(queue, cons, msg, refs, 0, true, referralConn);
135 else
137 references.Add(refs);
138 referenceCount++;
141 else
143 // LdapResponse
144 LdapResponse resp = (LdapResponse) msg;
145 int resultCode = resp.ResultCode;
146 // Check for an embedded exception
147 if (resp.hasException())
149 // Fake it, results in an exception when msg read
150 resultCode = LdapException.CONNECT_ERROR;
153 if ((resultCode == LdapException.REFERRAL) && cons.ReferralFollowing)
155 // Following referrals
156 // referralConn = conn.chaseReferral(queue, cons, resp, resp.Referrals, 0, false, referralConn);
158 else if (resultCode != LdapException.SUCCESS)
160 // Results in an exception when message read
161 entries.Add(resp);
162 entryCount++;
164 // We are done only when we have read all messages
165 // including those received from following referrals
166 int[] msgIDs = queue.MessageIDs;
167 if (msgIDs.Length == 0)
169 // Release referral exceptions
170 // conn.releaseReferralConnections(referralConn);
171 return true; // search completed
173 else
176 continue;
179 else
181 // We get here if the connection timed out
182 // we have no responses, no message IDs and no exceptions
183 LdapException e = new LdapException(null, LdapException.Ldap_TIMEOUT, (System.String) null);
184 entries.Add(e);
185 break;
188 catch (LdapException e)
190 // Hand exception off to user
191 entries.Add(e);
193 continue;
195 return false; // search not completed
200 private System.Collections.ArrayList entries; // Search entries
201 private int entryCount; // # Search entries in vector
202 private int entryIndex; // Current position in vector
203 private System.Collections.ArrayList references; // Search Result References
204 private int referenceCount; // # Search Result Reference in vector
205 private int referenceIndex; // Current position in vector
206 private int batchSize; // Application specified batch size
207 private bool completed = false; // All entries received
208 private LdapControl[] controls = null; // Last set of controls
209 private LdapSearchQueue queue;
210 private static System.Object nameLock; // protect resultsNum
211 private static int resultsNum = 0; // used for debug
212 private System.String name; // used for debug
213 private LdapConnection conn; // LdapConnection which started search
214 private LdapSearchConstraints cons; // LdapSearchConstraints for search
215 private System.Collections.ArrayList referralConn = null; // Referral Connections
217 /// <summary> Constructs a queue object for search results.
218 ///
219 /// </summary>
220 /// <param name="conn">The LdapConnection which initiated the search
221 ///
222 /// </param>
223 /// <param name="queue">The queue for the search results.
224 ///
225 /// </param>
226 /// <param name="cons">The LdapSearchConstraints associated with this search
227 /// </param>
228 /* package */
229 internal LdapSearchResults(LdapConnection conn, LdapSearchQueue queue, LdapSearchConstraints cons)
231 // setup entry Vector
232 this.conn = conn;
233 this.cons = cons;
234 int batchSize = cons.BatchSize;
235 int vectorIncr = (batchSize == 0)?64:0;
236 entries = new System.Collections.ArrayList((batchSize == 0)?64:batchSize);
237 entryCount = 0;
238 entryIndex = 0;
240 // setup search reference Vector
241 references = new System.Collections.ArrayList(5);
242 referenceCount = 0;
243 referenceIndex = 0;
245 this.queue = queue;
246 this.batchSize = (batchSize == 0)?System.Int32.MaxValue:batchSize;
248 return ;
251 /// <summary> Reports if there are more search results.
252 ///
253 /// </summary>
254 /// <returns> true if there are more search results.
255 /// </returns>
256 public virtual bool hasMore()
258 bool ret = false;
259 if ((entryIndex < entryCount) || (referenceIndex < referenceCount))
261 // we have data
262 ret = true;
264 else if (completed == false)
266 // reload the Vector by getting more results
267 resetVectors();
268 ret = (entryIndex < entryCount) || (referenceIndex < referenceCount);
270 return ret;
274 * If both of the vectors are empty, get more data for them.
276 private void resetVectors()
278 // If we're done, no further checking needed
279 if (completed)
281 return ;
283 // Checks if we have run out of references
284 if ((referenceIndex != 0) && (referenceIndex >= referenceCount))
286 SupportClass.SetSize(references, 0);
287 referenceCount = 0;
288 referenceIndex = 0;
290 // Checks if we have run out of entries
291 if ((entryIndex != 0) && (entryIndex >= entryCount))
293 SupportClass.SetSize(entries, 0);
294 entryCount = 0;
295 entryIndex = 0;
297 // If no data at all, must reload enumeration
298 if ((referenceIndex == 0) && (referenceCount == 0) && (entryIndex == 0) && (entryCount == 0))
300 completed = BatchOfResults;
302 return ;
304 /// <summary> Returns the next result as an LdapEntry.
305 ///
306 /// If automatic referral following is disabled or if a referral
307 /// was not followed, next() will throw an LdapReferralException
308 /// when the referral is received.
309 ///
310 /// </summary>
311 /// <returns> The next search result as an LdapEntry.
312 ///
313 /// </returns>
314 /// <exception> LdapException A general exception which includes an error
315 /// message and an Ldap error code.
316 /// </exception>
317 /// <exception> LdapReferralException A referral was received and not
318 /// followed.
319 /// </exception>
320 public virtual LdapEntry next()
322 if (completed && (entryIndex >= entryCount) && (referenceIndex >= referenceCount))
324 throw new System.ArgumentOutOfRangeException("LdapSearchResults.next() no more results");
326 // Check if the enumeration is empty and must be reloaded
327 resetVectors();
329 System.Object element = null;
330 // Check for Search References & deliver to app as they come in
331 // We only get here if not following referrals/references
332 if (referenceIndex < referenceCount)
334 System.String[] refs = (System.String[]) (references[referenceIndex++]);
335 LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_NOFOLLOW);
336 rex.setReferrals(refs);
337 throw rex;
339 else if (entryIndex < entryCount)
341 // Check for Search Entries and the Search Result
342 element = entries[entryIndex++];
343 if (element is LdapResponse)
345 // Search done w/bad status
346 if (((LdapResponse) element).hasException())
349 LdapResponse lr = (LdapResponse) element;
350 ReferralInfo ri = lr.ActiveReferral;
352 if (ri != null)
354 // Error attempting to follow a search continuation reference
355 LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_ERROR, lr.Exception);
356 rex.setReferrals(ri.ReferralList);
357 rex.FailedReferral = ri.ReferralUrl.ToString();
358 throw rex;
361 // Throw an exception if not success
362 ((LdapResponse) element).chkResultCode();
364 else if (element is LdapException)
366 throw (LdapException) element;
369 else
371 // If not a Search Entry, Search Result, or search continuation
372 // we are very confused.
373 // LdapSearchResults.next(): No entry found & request is not complete
374 throw new LdapException(ExceptionMessages.REFERRAL_LOCAL, new System.Object[]{"next"}, LdapException.LOCAL_ERROR, (System.String) null);
376 return (LdapEntry) element;
379 /// <summary> Cancels the search request and clears the message and enumeration.</summary>
380 /*package*/
381 internal virtual void Abandon()
383 // first, remove message ID and timer and any responses in the queue
384 queue.MessageAgent.AbandonAll();
386 // next, clear out enumeration
387 resetVectors();
388 completed = true;
390 static LdapSearchResults()
392 nameLock = new System.Object();