1
/** BEGIN COPYRIGHT BLOCK
2
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
3
* Copyright (C) 2005 Red Hat, Inc.
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
* END COPYRIGHT BLOCK **/
22
* This controller object displays LDAP entries inside a JList
23
* using a given LDAPConnection.
25
* Each time a IBrowserNodeInfo is passed, the controller
26
* sends some LDAP searches to retreive the child entries of the node and their
27
* attributes. The searches are done in background threads (without
28
* blocking the Event thread).
33
package com.netscape.admin.dirserv.browser;
35
import java.util.Vector;
36
import java.util.Enumeration;
37
import java.text.Collator;
38
import java.net.MalformedURLException;
39
import java.lang.reflect.InvocationTargetException;
41
import java.awt.Component;
43
import javax.swing.JList;
44
import javax.swing.Icon;
45
import javax.swing.SwingUtilities;
48
import netscape.ldap.LDAPConnection;
49
import netscape.ldap.LDAPException;
50
import netscape.ldap.LDAPAttribute;
51
import netscape.ldap.LDAPEntry;
52
import netscape.ldap.LDAPSearchResults;
53
import netscape.ldap.LDAPSearchConstraints;
54
import netscape.ldap.LDAPControl;
55
import netscape.ldap.LDAPSortKey;
56
import netscape.ldap.LDAPUrl;
57
import netscape.ldap.util.DN;
58
import netscape.ldap.controls.LDAPSortControl;
60
import com.netscape.management.client.util.Debug;
61
import com.netscape.management.client.util.RemoteImage;
63
import com.netscape.admin.dirserv.DSUtil;
65
public class ChildrenController {
67
ChildrenListModel _listModel;
70
boolean _followReferrals;
72
String[] _containerClasses;
75
LDAPConnectionPool _connectionPool;
77
LDAPSearchConstraints _searchConstraints;
78
BasicNode _parentNode;
80
boolean _parentHasIndex;
82
NodeTaskQueue _childrenQueue;
89
public ChildrenController(JList list,
90
LDAPConnectionPool pool,
93
_listModel = new ChildrenListModel();
94
_list.setModel(_listModel);
96
BasicNode prototypeNode = new BasicNode("");
97
prototypeNode.setIcon(new RemoteImage(
98
"com/netscape/admin/dirserv/images/person.gif"));
99
_list.setPrototypeCellValue(prototypeNode);
101
_iconPool = iconPool;
102
_displayFlags = BrowserController.DISPLAY_ACI_COUNT;
103
_followReferrals = true;
105
_containerClasses = new String[0];
106
_connectionPool = pool;
107
_searchConstraints = null; // Will be computed on the fly
109
_list.setCellRenderer(new ChildrenCellRenderer(this));
110
_listeners = new Vector(2);
113
_childrenQueue = new NodeTaskQueue("New child", 1);
118
* Set the connection for accessing the directory.
120
public void setLDAPConnection(LDAPConnection ldc) {
124
_connectionPool.registerAuth(_ldc);
125
rootNodeName = _ldc.getHost() + ":" + _ldc.getPort();
135
* Return the connection for accessing the directory.
137
public LDAPConnection getLDAPConnection() {
143
* Return the JList controlled by this controller.
145
public JList getList() {
151
* Return the connection pool used by this controller.
152
* If a client class adds authentication to the connection
153
* pool, it must inform the controller by calling
154
* notifyAuthDataChanged().
156
public LDAPConnectionPool getConnectionPool() {
157
return _connectionPool;
164
* Return the display flags.
166
public int getDisplayFlags() {
167
return _displayFlags;
172
* Set the display flags.
174
public void setDisplayFlags(int flags) {
175
_displayFlags = flags;
179
* Sets the maximum number of children to display for a node.
180
* 0 if there is no limit
182
public void setMaxChildren(int maxChildren) {
183
_maxChildren = maxChildren;
187
* Return the maximum number of children to display
189
public int getMaxChildren() {
195
* Return true if this controller follows referrals.
197
public boolean getFollowReferrals() {
198
return _followReferrals;
203
* Enable/display the following of referrals.
204
* This routine starts a refresh on each referral node.
206
public void setFollowReferrals(boolean yes) {
207
_followReferrals = yes;
208
startRefreshReferralNodes();
213
* Return true if entries are displayed sorted.
215
public boolean isSorted() {
221
* Enable/disable entry sort.
223
public void setSorted(boolean yes) {
226
_searchConstraints = null; // Force the reconstruction
231
* Find the IBrowserNodeInfo associated to an index and returns
232
* the describing IBrowserNodeInfo.
234
public IBrowserNodeInfo getNodeInfoFromIndex(int index) {
235
BasicNode node = (BasicNode)_listModel.getElementAt(index);
236
return new ChildrenNodeInfo(node);
241
* Return the array of container classes for this controller.
242
* Warning: the returned array is not cloned.
244
public String[] getContainerClasses() {
245
return _containerClasses;
250
* Set the list of container classes and calls startRefresh().
251
* Warning: the array is not cloned.
253
public void setContainerClasses(String[] containerClasses) {
254
_containerClasses = containerClasses;
260
* Add a BrowserEventListener to this controller.
262
public void addBrowserEventListener(BrowserEventListener l) {
263
_listeners.addElement(l);
268
* Remove a BrowserEventListener from this controller.
270
public void removeBrowserEventListener(BrowserEventListener l) {
271
_listeners.removeElement(l);
276
* Notify this controller that an entry has been added.
277
* The controller adds a new node in the JList at the end and
278
* starts refreshing this new node. There's no check about the validity
279
* of the dn of the added entry.
280
* This routine returns the index about the new entry.
282
public int notifyEntryAdded(String newEntryDn) {
283
BasicNode childNode = new BasicNode(newEntryDn);
284
int index = _listModel.getSize();
286
_listModel.addElement(childNode);
287
startRefreshNode(childNode);
288
if (!_parentHasIndex) {
289
_list.ensureIndexIsVisible(index);
296
* Notify this controller that a entry has been deleted.
297
* The controller removes the corresponding node from the
300
public void notifyEntryDeleted(IBrowserNodeInfo nodeInfo) {
301
if ( !(nodeInfo instanceof ChildrenNodeInfo)) {
304
BasicNode node = ((ChildrenNodeInfo)nodeInfo).getNode();
305
stopRefreshNode(node);
306
_listModel.removeElement(node);
311
* Notify this controller that an entry has changed.
312
* The controller starts refreshing the corresponding node.
313
* Child nodes are not refreshed.
315
public void notifyEntryChanged(IBrowserNodeInfo nodeInfo) {
316
if ( !(nodeInfo instanceof ChildrenNodeInfo)) {
319
BasicNode node = ((ChildrenNodeInfo)nodeInfo).getNode();
320
startRefreshNode(node);
321
_listModel.updateElement(node);
326
* Notify this controller that the entry DN has changed.
328
public void notifyEntryDNChanged(IBrowserNodeInfo nodeInfo, String newDN) {
329
if ( !(nodeInfo instanceof ChildrenNodeInfo)) {
332
BasicNode node = ((ChildrenNodeInfo)nodeInfo).getNode();
333
BasicNode newNode = new BasicNode(newDN);
334
startRefreshNode(newNode);
335
_listModel.replaceElement(node, newNode);
340
* Notify this controller that the VLV index presence has changed.
342
public void notifyIndexChanged(boolean hasIndex) {
343
_parentHasIndex = hasIndex;
348
* Notify this controller that authentication data
349
* have changed in the connection pool for the specified
351
* The controller starts refreshing the node which
352
* represent entries from the url.
354
public void notifyAuthDataChanged(LDAPUrl url) {
355
// TODO: temporary implementation
356
// we should refresh only nodes :
357
// - whose URL matches 'url'
358
// - whose errorType == ERROR_SOLVING_REFERRAL and
360
startRefreshReferralNodes();
364
* Updates the panel with a new parent. If node is null, empties the list.
366
public void setBaseNodeInfo(IBrowserNodeInfo node, boolean hasIndex) {
367
_parentHasIndex = hasIndex;
368
// We don't want to search for children of the
369
// root node since it doesn't represent an actual
371
if ((node == null) || node.isRootNode()) {
374
_parentNode = node.getNode();
381
* Refresh the whole panel
383
public void startRefresh() {
388
if (_parentNode != null) {
390
if (_parentHasIndex) {
393
_listModel = new VListModel(_parentNode, this, null);
394
_list.setModel(_listModel);
396
catch (LDAPException ex) {
397
Debug.println("ChildrenController.startRefresh " + ex);
401
_list.setModel(_listModel = new ChildrenListModel());
402
_childrenQueue.queue(new ChildrenTask(_parentNode, this));
408
* Start refreshing the node.
410
public void startRefresh(IBrowserNodeInfo node) {
411
startRefreshNode(node.getNode());
415
* Shutdown the controller.
417
public void shutDown() {
423
* Start refreshing the node.
425
public void startRefreshNode(BasicNode node) {
426
_childrenQueue.queue(new ChildrenTask(this, node));
430
* Start refreshing the node.
432
void startRefreshNode(BasicNode node, LDAPEntry entry) {
433
_childrenQueue.queue(new ChildrenTask(this, node, entry));
438
_childrenQueue.cancelAll();
443
* Stop refreshing below this node.
445
void stopRefreshNode(BasicNode node) {
446
_childrenQueue.cancelForNode(node);
452
* Call startRefreshNode() on each referral node accessible
455
void startRefreshReferralNodes() {
456
Enumeration e = _listModel.elements();
457
while (e.hasMoreElements()) {
458
BasicNode child = (BasicNode)e.nextElement();
459
if ((child.getReferral() != null) || (child.getRemoteUrl() != null)) {
460
startRefreshNode(child);
468
* Return the icon pool used by this controller.
470
IconPool getIconPool() {
475
* Return the LDAP search filter to use for searching child entries.
476
* If _showContainerOnly is true, the filter will select only the
477
* container entries. If not, the filter will select all the children.
479
String getChildSearchFilter() {
480
String result = "(|(objectclass=*)(objectclass=ldapsubentry))";
486
* Return the LDAP connection to reading the base entry of a node.
488
LDAPConnection findConnectionForLocalEntry(BasicNode node)
489
throws LDAPException {
490
LDAPConnection result;
491
if (node instanceof RootNode) {
493
} else if (node.getParent() != null) {
494
result = findConnectionForDisplayedEntry((BasicNode)node.getParent());
495
} else if ((_parentNode != null) &&
496
(node != _parentNode)) {
497
result = findConnectionForDisplayedEntry(_parentNode);
507
* Return the LDAP connection to search the displayed entry
508
* (which can be the local or remote entry).
510
LDAPConnection findConnectionForDisplayedEntry(BasicNode node)
511
throws LDAPException {
512
LDAPConnection result;
513
if (_followReferrals && (node.getRemoteUrl() != null)) {
514
result = _connectionPool.getConnection(node.getRemoteUrl());
517
result = findConnectionForLocalEntry(node);
525
* Release a connection returned by
526
* selectConnectionForChildEntries()
528
* selectConnectionForBaseEntry()
530
void releaseLDAPConnection(LDAPConnection ldc) {
531
if (ldc != _ldc) { // Thus it comes from the connection pool
532
_connectionPool.releaseConnection(ldc);
540
LDAPUrl findUrlForLocalEntry(BasicNode node, int searchDepth) {
541
LDAPUrl result = null;
542
if (node instanceof RootNode) {
543
result = LDAPConnectionPool.makeLDAPUrl(_ldc, "");
544
} else if (node.getParent() != null) {
545
BasicNode parent = (BasicNode)node.getParent();
546
LDAPUrl parentUrl = findUrlForDisplayedEntry(parent, searchDepth);
547
result = LDAPConnectionPool.makeLDAPUrl(parentUrl, node.getDN());
548
} else if (_parentNode != null && searchDepth == 1) {
549
LDAPUrl parentUrl = findUrlForDisplayedEntry(_parentNode, searchDepth);
550
result = LDAPConnectionPool.makeLDAPUrl(parentUrl, node.getDN());
553
result = LDAPConnectionPool.makeLDAPUrl(_ldc, "");
562
LDAPUrl findUrlForDisplayedEntry(BasicNode node, int searchDepth) {
564
// (miodrag) searchDepth was added as a safty brake to this method and
565
// findUrlForLocalEntry as under some circumstances (do not know how to
566
// reproduce it) there will be a endless loop between the two methods
567
// calling each other.
568
if (++searchDepth > 100) {
569
return LDAPConnectionPool.makeLDAPUrl(_ldc, "");
573
if (_followReferrals && (node.getRemoteUrl() != null)) {
574
result = node.getRemoteUrl();
577
result = findUrlForLocalEntry(node, searchDepth);
584
* Returns the DN to use for searching children of a given node.
585
* In most cases, it's node.getDN(). However if node has referral data
586
* and _followReferrals is true, the result is calculated from the
587
* referral resolution.
589
String findBaseDNForChildEntries(BasicNode node) {
592
if (_followReferrals && (node.getRemoteUrl() != null)) {
593
result = node.getRemoteUrl().getDN();
596
result = node.getDN();
604
boolean isDisplayedEntryRemote(BasicNode node) {
605
boolean result = false;
606
if (_followReferrals &&
608
if (node instanceof RootNode) {
610
} else if (node.getRemoteUrl() != null) {
613
if (node.getParent() != null) {
614
result = isDisplayedEntryRemote((BasicNode)node.getParent());
615
} else if ((_parentNode != null) &&
616
(node != _parentNode)) {
618
if (node instanceof SuffixNode) {
622
result = isDisplayedEntryRemote(_parentNode);
633
* Returns the list of attributes for the red search.
635
String[] getAttrsForRedSearch() {
636
Vector v = new Vector();
638
v.addElement("objectclass");
639
v.addElement("numsubordinates");
641
if ((_displayFlags & BrowserController.DISPLAY_ACI_COUNT) != 0) {
644
if ((_displayFlags & BrowserController.DISPLAY_ROLE_COUNT) != 0) {
645
v.addElement("nsrole");
647
if ((_displayFlags & BrowserController.DISPLAY_ACTIVATION_STATE) != 0) {
648
v.addElement("nsaccountlock");
651
String[] result = new String[v.size()];
657
* Returns the list of attributes for the black search.
659
String[] getAttrsForBlackSearch() {
660
return new String[] {
671
* Returns the list of attributes for the black search.
673
LDAPSearchConstraints getSearchConstraints() {
674
if (_searchConstraints == null) {
675
LDAPControl ctls[] = new LDAPControl[_sorted ? 2 : 1];
676
ctls[0] = new LDAPControl(LDAPControl.MANAGEDSAIT, true, null);
678
LDAPSortKey[] keys = new LDAPSortKey[BrowserController.SORT_ATTRIBUTES.length];
679
for (int i=0; i<keys.length; i++) {
680
keys[i] = new LDAPSortKey(BrowserController.SORT_ATTRIBUTES[i]);
682
ctls[1] = new LDAPSortControl(keys, true);
684
_searchConstraints = (LDAPSearchConstraints)_ldc.getSearchConstraints().clone();
685
_searchConstraints.setMaxResults(_maxChildren);
686
_searchConstraints.setServerControls(ctls); // Return referral entries
688
return _searchConstraints;
693
* Callbacks invoked by task classes
694
* =================================
696
* The routines below are invoked by the task classes; they
697
* update the nodes and the tree model.
699
* To ensure the consistency of the tree model, these routines
700
* are not invoked directly by the task classes: they are
701
* invoked using SwingUtilities.invokeAndWait() (each of the
702
* methods XXX() below has a matching wrapper invokeXXX()).
708
* Invoked when the refresh task has finished the red operation:
709
* it has read the attributes of the base entry ; the result of the
711
* - an LDAPEntry if successful
712
* - an Exception if failed
715
private void childrenTaskDidProgress(ChildrenTask task, int oldState, int newState) {
716
BasicNode node = task.getNode();
717
boolean nodeChanged = false;
720
if (oldState == ChildrenTask.QUEUED) {
721
checkUpdateEvent(true);
723
if (task.isInFinalState()) {
724
checkUpdateEvent(false);
727
if (newState == ChildrenTask.FAILED) {
728
if (oldState == ChildrenTask.SOLVING_REFERRAL) {
729
node.setRemoteUrl(task.getRemoteUrl());
730
if (task.getRemoteEntry() != null) {
731
/* This is the case when there are multiple hops in the referral
732
and so we have a remote referral entry but not the entry that it
734
updateNodeRendering(node, task.getRemoteEntry());
736
node.setError(new BasicNode.Error(oldState, task.getException(), task.getExceptionArg()));
737
nodeChanged = updateNodeRendering(node, task.getDisplayedEntry());
739
else if (oldState == ChildrenTask.SEARCHING_CHILDREN) {
740
addErrorNode(task, oldState);
742
} else if (newState == ChildrenTask.CANCELLED) {
743
/* Clear the list... */
747
if (oldState == ChildrenTask.READING_LOCAL_ENTRY) {
748
/* The task is going to try to solve the referral if there's one.
749
If succeeds we will update the remote url. Set it to null for
750
the case when there was a referral and it has been deleted */
751
node.setRemoteUrl(null);
752
LDAPEntry localEntry = task.getLocalEntry();
753
if (localEntry == null) {
754
_listModel.removeElement(task.getNode()); // entry deleted
757
nodeChanged = updateNodeRendering(node, localEntry);
760
else if (oldState == ChildrenTask.SOLVING_REFERRAL) {
761
node.setRemoteUrl(task.getRemoteUrl());
762
updateNodeRendering(node, task.getRemoteEntry());
765
else if (oldState == ChildrenTask.SEARCHING_CHILDREN) {
766
updateChildNodes(task);
769
if (newState == ChildrenTask.FINISHED) {
770
if ((node.getError() != null) &&
771
(node != _parentNode)) {
773
nodeChanged = updateNodeRendering(node, task.getDisplayedEntry());
778
_listModel.updateElement(task.getNode());
784
void invokeChildrenTaskDidProgress(final ChildrenTask task, final int oldState, final int newState)
785
throws InterruptedException {
787
Runnable r = new Runnable() {
788
ChildrenTask _task = task;
789
int _oldState = oldState;
790
int _newState = newState;
793
childrenTaskDidProgress(_task, _oldState, _newState);
806
* Core routines shared by the callbacks above
807
* ===========================================
811
private void updateChildNodes(ChildrenTask task) {
812
BasicNode parent = task.getNode();
814
// Walk through the entries
815
Enumeration e = task.getChildEntries().elements();
816
//Vector newEntries = new Vector(task.getChildEntries().size());
817
while (e.hasMoreElements()) {
818
LDAPEntry entry = (LDAPEntry) e.nextElement();
821
child = new BasicNode(entry.getDN());
822
_listModel.addElement(child);
823
updateNodeRendering(child, entry);
825
// Propagate the refresh
826
// Note: logically we should unconditionaly call:
827
// startRefreshNode(child, false, true);
829
// However doing that saturates _redQueue
830
// with many nodes. And, by design, ChildrenTask
831
// won't do anything on a node if:
832
// - this node has no subordinates
833
// - *and* this node has no referral data
834
// So we test these conditions here and
835
// skip the call to startRefreshNode() if
837
if (child.getReferral() != null) {
838
startRefreshNode(child, entry);
841
//newEntries.add(child);
844
//_listModel.addElements(newEntries);
848
* Add error node if a limit exceeded error was received when
849
* listing children. This is an indication that the list was
850
* truncated and that a browsing index is required.
852
void addErrorNode(ChildrenTask task, int oldState) {
853
Exception ex = task.getException();
854
if (ex == null || !(ex instanceof LDAPException)) {
858
LDAPException lex = (LDAPException) task.getException();
859
int rc = lex.getLDAPResultCode();
860
String dspName = null;
862
if (rc == lex.NO_SUCH_OBJECT) {
866
boolean limitError = rc == lex.ADMIN_LIMIT_EXCEEDED ||
867
rc == lex.TIME_LIMIT_EXCEEDED ||
868
rc == lex.SIZE_LIMIT_EXCEEDED;
870
if (rc == lex.ADMIN_LIMIT_EXCEEDED) {
871
dspName = DSUtil._resource.getString("browser", "admin-limit-err");
873
else if (rc == lex.TIME_LIMIT_EXCEEDED) {
874
dspName = DSUtil._resource.getString("browser", "time-limit-err");
876
else if (rc == lex.SIZE_LIMIT_EXCEEDED) {
877
dspName = DSUtil._resource.getString("browser", "size-limit-err");
880
dspName = lex.errorCodeToString();
883
if (limitError && !_parentHasIndex) {
884
dspName += " " + DSUtil._resource.getString("browser", "no-index-err");
887
BasicNode node = new BasicNode("ERROR-NODE");
888
node.setIcon(_iconPool.getErrorIcon());
889
node.setDisplayName(dspName);
890
node.setError(new BasicNode.Error(oldState, task.getException(), task.getExceptionArg()));
892
_listModel.addElement(node);
896
* Create a list node from an LDAP entry
898
BasicNode createNodeFromEntry(LDAPEntry entry) {
899
BasicNode node = null;
901
node = new BasicNode("BOGUS_NODE"); // VListModel bogus node
904
node = new BasicNode(entry.getDN());
906
updateNodeRendering(node, entry);
907
// Propagate the refresh
908
// Note: logically we should unconditionaly call:
909
// startRefreshNode(child, false, true);
911
// However doing that saturates _redQueue
912
// with many nodes. And, by design, ChildrenTask
913
// won't do anything on a node if:
914
// - this node has no subordinates
915
// - *and* this node has no referral data
916
// So we test these conditions here and
917
// skip the call to startRefreshNode() if
919
if (node.getReferral() != null) {
920
startRefreshNode(node, entry);
927
* Recompute the rendering props of a node (text, style, icon) according:
928
* - the state of this node
929
* - the LDAPEntry displayed by this node
931
private boolean updateNodeRendering(BasicNode node, LDAPEntry entry) {
933
// Get the numsubordinates
934
node.setNumSubOrdinates(getNumSubOrdinates(entry));
935
node.setReferral(BrowserController.getReferral(entry));
940
if (((_displayFlags & BrowserController.DISPLAY_ACI_COUNT) != 0) && (entry != null)) {
941
LDAPAttribute aciAttr = entry.getAttribute("aci");
942
if (aciAttr != null) {
943
aciCount = aciAttr.size();
953
// Get the role count
955
if (((_displayFlags & BrowserController.DISPLAY_ROLE_COUNT) != 0) && (entry != null)) {
956
LDAPAttribute roleAttr = entry.getAttribute("nsrole");
957
if (roleAttr != null) {
958
roleCount = roleAttr.size();
969
// Get the nsaccountlock
970
boolean nsaccountlock;
971
if (((_displayFlags & BrowserController.DISPLAY_ACTIVATION_STATE) != 0) && (entry != null)) {
972
LDAPAttribute lockAttr = entry.getAttribute("nsaccountlock");
973
if (lockAttr != null) {
974
String[] values = lockAttr.getStringValueArray();
975
if (values.length >= 1) {
976
nsaccountlock = values[0].equalsIgnoreCase("true");
978
/* Check if is a role... in this case nsaccountlock
980
LDAPAttribute objectclassAttr = entry.getAttribute("objectclass");
981
if (objectclassAttr != null) {
982
Enumeration e = objectclassAttr.getStringValues();
984
boolean isRoleDefinition=false;
985
boolean isLDAPSubentry=false;
986
while (e.hasMoreElements() &&
987
(!isRoleDefinition || !isLDAPSubentry)) {
988
value = (String)e.nextElement();
989
if (value.equalsIgnoreCase("nsroledefinition")) {
990
isRoleDefinition=true;
991
} else if (value.equalsIgnoreCase("ldapsubentry")) {
992
isLDAPSubentry = true;
995
/* It's a role -> we don't know about the state of the role */
996
if (isRoleDefinition && isLDAPSubentry) {
997
nsaccountlock = false;
1003
nsaccountlock = false;
1007
nsaccountlock = false;
1011
nsaccountlock = false;
1015
// Select the icon according the objectclass,...
1017
if (node.isLeaf() && (node.getNumSubOrdinates() <= 0)) {
1018
modifiers |= _iconPool.MODIFIER_LEAF;
1020
if (node.getReferral() != null) {
1021
modifiers |= _iconPool.MODIFIER_REFERRAL;
1023
if (node.getError() != null) {
1024
modifiers |= _iconPool.MODIFIER_ERROR;
1026
if (nsaccountlock) {
1027
modifiers |= _iconPool.MODIFIER_INACTIVATED;
1029
LDAPAttribute objectClass = null;
1030
if (entry != null) {
1031
objectClass = entry.getAttribute("objectclass");
1033
Icon newIcon = _iconPool.getIcon(objectClass, modifiers);
1035
// Contruct the icon text according the dn, the aci count...
1036
StringBuffer sb2 = new StringBuffer();
1037
if (aciCount >= 1) {
1038
sb2.append(String.valueOf(aciCount));
1039
if (aciCount == 1) {
1043
sb2.append(" acis");
1046
if (roleCount >= 1) {
1047
if (sb2.length() >= 1) sb2.append(", ");
1048
sb2.append(String.valueOf(roleCount));
1049
if (roleCount == 1) {
1050
sb2.append(" role");
1053
sb2.append(" roles");
1056
StringBuffer sb1 = new StringBuffer();
1058
if (_followReferrals && (node.getRemoteUrl() != null)) {
1059
rdn = node.getRemoteRDN();
1062
rdn = node.getRDN();
1065
if (sb2.length() >= 1) {
1070
String newDisplayName = sb1.toString();
1072
// Select the font style according referral
1074
if (isDisplayedEntryRemote(node)) {
1075
newStyle |= Font.ITALIC;
1077
if (node instanceof SuffixNode) {
1078
newStyle |= Font.BOLD;
1081
// Determine if the rendering needs to be updated
1083
(node.getIcon() != newIcon) ||
1084
(node.getDisplayName() != newDisplayName) ||
1085
(node.getFontStyle() != newStyle)
1088
node.setIcon(newIcon);
1089
node.setDisplayName(newDisplayName);
1090
node.setFontStyle(newStyle);
1099
* Find a child node matching a given DN.
1100
* Choose the most efficient way according the state of the _sorted property.
1102
* result >= 0 result is the index of the node matching childDn
1103
* result < 0 -(result + 1) is the index at which the new node must be inserted
1105
int findChildNode(BasicNode parent, String childDn) {
1108
result = findSortedChildNode(parent, childDn, 0, parent.getChildCount()-1);
1111
result = findUnSortedChildNode(parent, childDn);
1119
int findSortedChildNode(BasicNode parent, String childDn, int start, int end) {
1121
// INVARIANT : start-1 < childDn < end+1
1123
if (start != end+1) throw new IllegalStateException("Bug in findSortedChildNode");
1124
result = -(start+1);
1127
int cr = compareDnToChildNode(childDn, parent, start);
1128
if (cr < 0) { // start-1 < childDn < start
1129
result = -(start + 1);
1131
else if (cr == 0) { // childDn == start
1134
else if (start == end) { // end < childDn < end+1
1135
result = -(end+1 + 1);
1137
else { // childDn must be compared to the 'end' child
1138
cr = compareDnToChildNode(childDn, parent, end);
1139
if (cr > 0) { // end < childDn < end+1
1140
result = -(end+1 + 1);
1142
else if (cr == 0) { // childDn == end;
1145
else { // start < childDn < end
1146
// childDn must be compared with the median child
1147
int median = (start + end) / 2;
1148
cr = compareDnToChildNode(childDn, parent, median);
1149
if (cr < 0) { // start < childDn < median
1150
result = findSortedChildNode(parent, childDn, start+1, median-1);
1152
else if (cr == 0) { // childDn == median
1155
else { // median < childDn < end
1156
result = findSortedChildNode(parent, childDn, median+1, end-1);
1166
* We assume that parent.getChildCount() >= 1
1168
int findUnSortedChildNode(BasicNode parent, String childDn) {
1169
int childCount = parent.getChildCount();
1171
while ((i < childCount) &&
1172
!childDn.equals(((BasicNode)parent.getChildAt(i)).getDN())) {
1175
if (i >= childCount) { // Not found
1176
i = -(childCount + 1);
1184
* Follow the String.compare() result convention.
1185
* result < 0 -> dn < parent.getChild[childIndex]
1186
* result == 0 -> dn == parent.getChild[childIndex]
1187
* result > 0 -> dn > parent.getChild[childIndex]
1189
int compareDnToChildNode(String dn, BasicNode parent, int childIndex) {
1190
BasicNode node = (BasicNode)parent.getChildAt(childIndex);
1191
return Collator.getInstance().compare(dn, node.getDN());
1197
* BrowserEvent management
1198
* =======================
1200
* This method computes the total size of the queues,
1201
* compares this value with the last computed and
1202
* decides if an update event should be fired or not.
1204
* It's invoked by task classes through SwingUtilities.invokeLater()
1205
* (see the wrapper below). That means the event handling routine
1206
* (processBrowserEvent) is executed in the event thread.
1208
private void checkUpdateEvent(boolean taskIsStarting) {
1209
int newSize = _childrenQueue.size();
1210
if (!taskIsStarting) {
1211
newSize = newSize - 1;
1213
if (newSize != _queueTotalSize) {
1214
if ((_queueTotalSize == 0) && (newSize >= 1)) {
1215
fireEvent(BrowserEvent.UPDATE_START);
1217
else if ((_queueTotalSize >= 1) && (newSize == 0)) {
1218
fireEvent(BrowserEvent.UPDATE_END);
1220
_queueTotalSize = newSize;
1225
private void fireEvent(int id) {
1226
BrowserEvent event = new BrowserEvent(this, id);
1227
Enumeration e = _listeners.elements();
1228
while (e.hasMoreElements()) {
1229
BrowserEventListener l = (BrowserEventListener)e.nextElement();
1230
l.processBrowserEvent(event);
1236
* Miscellaneous private routines
1237
* ==============================
1243
* Return true if x is an LDAPException with resultCode = NO_SUCH_OBJECT.
1245
private boolean isNoSuchObjectLDAPException(Object x) {
1247
if (x instanceof LDAPException) {
1248
LDAPException lx = (LDAPException)x;
1249
result = (lx.getLDAPResultCode() == lx.NO_SUCH_OBJECT);
1260
* If numsubordinates is not available, returns -1.
1262
public static int getNumSubOrdinates(LDAPEntry entry) {
1265
LDAPAttribute attr = entry.getAttribute("numsubordinates");
1271
result = Integer.parseInt(attr.getStringValueArray()[0]);
1273
catch(NumberFormatException x) {
1284
static int[] intArrayFromVector(Vector v) {
1285
int[] result = new int[v.size()];
1286
for (int i = 0; i < result.length; i++) {
1287
result[i] = ((Integer)v.elementAt(i)).intValue();
1292
static LDAPEntry[] entryArrayFromVector(Vector v) {
1293
LDAPEntry[] result = new LDAPEntry[v.size()];
1298
static BasicNode[] nodeArrayFromVector(Vector v) {
1299
BasicNode[] result = new BasicNode[v.size()];
1307
* For debugging purpose: allows to switch easily
1308
* between invokeLater() and invokeAndWait() for
1309
* experimentation...
1311
static void swingInvoke(Runnable r)
1312
throws InterruptedException {
1314
SwingUtilities.invokeAndWait(r);
1316
catch(InterruptedException x) {
1319
catch(InvocationTargetException x) {
1320
// Probably a very big trouble...
1321
x.printStackTrace();
1327
class ChildrenNodeInfo implements IBrowserNodeInfo {
1333
boolean _isRootNode;
1335
int _numSubOrdinates;
1337
Exception _errorException;
1341
public ChildrenNodeInfo(BasicNode node) {
1343
_url = findUrlForDisplayedEntry(node, 0);
1345
_isRootNode = node instanceof RootNode;
1346
_isRemote = isDisplayedEntryRemote(node);
1347
_isSuffix = node instanceof SuffixNode;
1348
_referral = node.getReferral();
1349
_numSubOrdinates = node.getNumSubOrdinates();
1350
if (node.getError() != null) {
1351
BasicNode.Error error = node.getError();
1352
switch(error.getType()) {
1353
case ChildrenTask.READING_LOCAL_ENTRY:
1354
_errorType = ERROR_READING_ENTRY;
1356
case ChildrenTask.SOLVING_REFERRAL:
1357
_errorType = ERROR_SOLVING_REFERRAL;
1359
case ChildrenTask.SEARCHING_CHILDREN:
1360
_errorType = ERROR_SEARCHING_CHILDREN;
1364
_errorException = error.getException();
1365
_errorArg = error.getArg();
1369
public BasicNode getNode() {
1374
public LDAPUrl getURL() {
1378
public boolean isRootNode() {
1382
public boolean isSuffix() {
1386
public boolean isRemote() {
1390
public String[] getReferral() {
1394
public int getNumSubOrdinates() {
1395
return _numSubOrdinates;
1398
public int getErrorType() {
1402
public Exception getErrorException() {
1403
return _errorException;
1406
public Object getErrorArg() {
1410
public javax.swing.tree.TreePath getTreePath() {
1414
public String toString() {
1415
StringBuffer sb = new StringBuffer();
1416
sb.append(getURL());
1417
if (getReferral() != null) {
1419
sb.append(getReferral());
1421
return sb.toString();