001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.util; 018 019import static org.opends.admin.ads.util.ConnectionUtils.*; 020import static org.opends.messages.AdminToolMessages.*; 021import static org.opends.quicksetup.Installation.*; 022import static com.forgerock.opendj.cli.Utils.*; 023import static com.forgerock.opendj.util.OperatingSystem.*; 024 025import java.awt.Color; 026import java.awt.Component; 027import java.awt.Container; 028import java.awt.Dimension; 029import java.awt.Font; 030import java.awt.Image; 031import java.awt.Point; 032import java.awt.Toolkit; 033import java.awt.Window; 034import java.awt.event.MouseAdapter; 035import java.awt.event.MouseEvent; 036import java.io.File; 037import java.io.IOException; 038import java.io.UnsupportedEncodingException; 039import java.text.CharacterIterator; 040import java.text.StringCharacterIterator; 041import java.util.ArrayList; 042import java.util.Collection; 043import java.util.Comparator; 044import java.util.Date; 045import java.util.List; 046import java.util.regex.Pattern; 047 048import javax.naming.CompositeName; 049import javax.naming.InvalidNameException; 050import javax.naming.Name; 051import javax.naming.NamingEnumeration; 052import javax.naming.NamingException; 053import javax.naming.directory.SearchControls; 054import javax.naming.directory.SearchResult; 055import javax.naming.ldap.InitialLdapContext; 056import javax.naming.ldap.LdapName; 057import javax.swing.BorderFactory; 058import javax.swing.DefaultComboBoxModel; 059import javax.swing.ImageIcon; 060import javax.swing.JButton; 061import javax.swing.JCheckBox; 062import javax.swing.JComboBox; 063import javax.swing.JComponent; 064import javax.swing.JDialog; 065import javax.swing.JEditorPane; 066import javax.swing.JFrame; 067import javax.swing.JLabel; 068import javax.swing.JMenu; 069import javax.swing.JMenuItem; 070import javax.swing.JOptionPane; 071import javax.swing.JPasswordField; 072import javax.swing.JRadioButton; 073import javax.swing.JScrollPane; 074import javax.swing.JTable; 075import javax.swing.JTextArea; 076import javax.swing.JTextField; 077import javax.swing.SwingConstants; 078import javax.swing.SwingUtilities; 079import javax.swing.border.Border; 080import javax.swing.border.EmptyBorder; 081import javax.swing.border.EtchedBorder; 082import javax.swing.border.TitledBorder; 083import javax.swing.table.JTableHeader; 084import javax.swing.table.TableCellRenderer; 085import javax.swing.table.TableColumn; 086import javax.swing.table.TableColumnModel; 087 088import org.forgerock.i18n.LocalizableMessage; 089import org.forgerock.i18n.slf4j.LocalizedLogger; 090import org.forgerock.opendj.config.ConfigurationFramework; 091import org.forgerock.opendj.config.server.ConfigException; 092import org.forgerock.opendj.ldap.schema.AttributeType; 093import org.forgerock.opendj.ldap.schema.MatchingRule; 094import org.forgerock.opendj.ldap.schema.Syntax; 095import org.opends.guitools.controlpanel.ControlPanel; 096import org.opends.guitools.controlpanel.browser.IconPool; 097import org.opends.guitools.controlpanel.datamodel.CategorizedComboBoxElement; 098import org.opends.guitools.controlpanel.datamodel.ConfigReadException; 099import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 100import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 101import org.opends.guitools.controlpanel.datamodel.MonitoringAttributes; 102import org.opends.guitools.controlpanel.datamodel.SortableTableModel; 103import org.opends.guitools.controlpanel.datamodel.VLVIndexDescriptor; 104import org.opends.guitools.controlpanel.event.ClickTooltipDisplayer; 105import org.opends.guitools.controlpanel.event.ComboKeySelectionManager; 106import org.opends.guitools.controlpanel.event.TextComponentFocusListener; 107import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; 108import org.opends.guitools.controlpanel.ui.components.LabelWithHelpIcon; 109import org.opends.guitools.controlpanel.ui.components.SelectableLabelWithHelpIcon; 110import org.opends.guitools.controlpanel.ui.renderer.AccessibleTableHeaderRenderer; 111import org.opends.quicksetup.Installation; 112import org.opends.quicksetup.ui.UIFactory; 113import org.opends.quicksetup.util.Utils; 114import org.opends.server.admin.ClassLoaderProvider; 115import org.opends.server.api.ConfigHandler; 116import org.opends.server.config.ConfigEntry; 117import org.opends.server.core.LockFileManager; 118import org.opends.server.schema.SchemaConstants; 119import org.opends.server.schema.SomeSchemaElement; 120import org.forgerock.opendj.ldap.DN; 121import org.opends.server.types.OpenDsException; 122import org.opends.server.types.Schema; 123import org.opends.server.util.ServerConstants; 124import org.opends.server.util.StaticUtils; 125 126/** 127 * A static class that provides miscellaneous functions. 128 */ 129public class Utilities 130{ 131 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 132 133 private static File rootDirectory; 134 private static File instanceRootDirectory; 135 136 private static final String HTML_SPACE = " "; 137 private static final String[] attrsToObfuscate = { ServerConstants.ATTR_USER_PASSWORD }; 138 private static final String[] passwordSyntaxOIDs = { SchemaConstants.SYNTAX_USER_PASSWORD_OID }; 139 private static final String[] binarySyntaxOIDs = { 140 SchemaConstants.SYNTAX_BINARY_OID, 141 SchemaConstants.SYNTAX_JPEG_OID, 142 SchemaConstants.SYNTAX_CERTIFICATE_OID, 143 SchemaConstants.SYNTAX_OCTET_STRING_OID 144 }; 145 146 private static ImageIcon warningIcon; 147 private static ImageIcon requiredIcon; 148 149 private final static LocalizableMessage NO_VALUE_SET = INFO_CTRL_PANEL_NO_MONITORING_VALUE.get(); 150 private final static LocalizableMessage NOT_IMPLEMENTED = INFO_CTRL_PANEL_NOT_IMPLEMENTED.get(); 151 152 /** 153 * Creates a combo box. 154 * 155 * @param <T> 156 * The combo box data type. 157 * @return a combo box. 158 */ 159 public static <T> JComboBox<T> createComboBox() 160 { 161 JComboBox<T> combo = new JComboBox<>(); 162 if (isMacOS()) 163 { 164 combo.setOpaque(false); 165 } 166 combo.setKeySelectionManager(new ComboKeySelectionManager(combo)); 167 return combo; 168 } 169 170 /** 171 * Creates a frame. 172 * @return a frame. 173 */ 174 public static JFrame createFrame() 175 { 176 JFrame frame = new JFrame(); 177 frame.setResizable(true); 178 org.opends.quicksetup.ui.Utilities.setFrameIcon(frame); 179 return frame; 180 } 181 182 /** 183 * Returns <CODE>true</CODE> if an attribute value must be obfuscated because 184 * it contains sensitive information (like passwords) and <CODE>false</CODE> 185 * otherwise. 186 * @param attrName the attribute name. 187 * @param schema the schema of the server. 188 * @return <CODE>true</CODE> if an attribute value must be obfuscated because 189 * it contains sensitive information (like passwords) and <CODE>false</CODE> 190 * otherwise. 191 */ 192 public static boolean mustObfuscate(String attrName, Schema schema) 193 { 194 if (schema != null) 195 { 196 return hasPasswordSyntax(attrName, schema); 197 } 198 for (String attr : attrsToObfuscate) 199 { 200 if (attr.equalsIgnoreCase(attrName)) 201 { 202 return true; 203 } 204 } 205 return false; 206 } 207 208 /** 209 * Derives a color by adding the specified offsets to the base color's 210 * hue, saturation, and brightness values. The resulting hue, saturation, 211 * and brightness values will be constrained to be between 0 and 1. 212 * @param base the color to which the HSV offsets will be added 213 * @param dH the offset for hue 214 * @param dS the offset for saturation 215 * @param dB the offset for brightness 216 * @return Color with modified HSV values 217 */ 218 public static Color deriveColorHSB(Color base, float dH, float dS, float dB) 219 { 220 float hsb[] = Color.RGBtoHSB( 221 base.getRed(), base.getGreen(), base.getBlue(), null); 222 223 hsb[0] += dH; 224 hsb[1] += dS; 225 hsb[2] += dB; 226 return Color.getHSBColor( 227 hsb[0] < 0? 0 : (hsb[0] > 1? 1 : hsb[0]), 228 hsb[1] < 0? 0 : (hsb[1] > 1? 1 : hsb[1]), 229 hsb[2] < 0? 0 : (hsb[2] > 1? 1 : hsb[2])); 230 231 } 232 233 /** 234 * Displays an error dialog that contains a set of error messages. 235 * @param parentComponent the parent component relative to which the dialog 236 * will be displayed. 237 * @param errors the set of error messages that the dialog must display. 238 */ 239 public static void displayErrorDialog(Component parentComponent, 240 Collection<LocalizableMessage> errors) 241 { 242 /* 243 ErrorPanel panel = new ErrorPanel("Error", errors); 244 GenericDialog dlg = new GenericDialog(null, panel); 245 dlg.setModal(true); 246 Utilities.centerGoldenMean(dlg, Utilities.getParentDialog(this)); 247 dlg.setVisible(true); 248 */ 249 ArrayList<String> stringErrors = new ArrayList<>(); 250 for (LocalizableMessage err : errors) 251 { 252 stringErrors.add(err.toString()); 253 } 254 String msg = getStringFromCollection(stringErrors, "<br>"); 255 String plainText = msg.replaceAll("<br>", ServerConstants.EOL); 256 String wrappedText = wrapText(plainText, 70); 257 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 258 JOptionPane.showMessageDialog( 259 parentComponent, "<html>"+wrappedText, 260 INFO_CTRL_PANEL_ERROR_DIALOG_TITLE.get().toString(), 261 JOptionPane.ERROR_MESSAGE); 262 } 263 264 /** 265 * Displays a confirmation dialog. Returns <CODE>true</CODE> if the user 266 * accepts the message and <CODE>false</CODE> otherwise. 267 * @param parentComponent the parent component relative to which the dialog 268 * will be displayed. 269 * @param title the title of the dialog. 270 * @param msg the message to be displayed. 271 * @return <CODE>true</CODE> if the user accepts the message and 272 * <CODE>false</CODE> otherwise. 273 * 274 */ 275 public static boolean displayConfirmationDialog(Component parentComponent, 276 LocalizableMessage title, LocalizableMessage msg) 277 { 278 String plainText = msg.toString().replaceAll("<br>", ServerConstants.EOL); 279 String wrappedText = wrapText(plainText, 70); 280 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 281 return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog( 282 parentComponent, "<html>"+wrappedText, 283 title.toString(), 284 JOptionPane.YES_NO_OPTION, 285 JOptionPane.QUESTION_MESSAGE, 286 null, // don't use a custom Icon 287 null, // the titles of buttons 288 null); // default button title 289 } 290 291 /** 292 * Displays a warning dialog. 293 * @param parentComponent the parent component relative to which the dialog 294 * will be displayed. 295 * @param title the title of the dialog. 296 * @param msg the message to be displayed. 297 */ 298 public static void displayWarningDialog(Component parentComponent, 299 LocalizableMessage title, LocalizableMessage msg) 300 { 301 String plainText = msg.toString().replaceAll("<br>", ServerConstants.EOL); 302 String wrappedText = wrapText(plainText, 70); 303 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 304 JOptionPane.showMessageDialog( 305 parentComponent, "<html>"+wrappedText, 306 title.toString(), 307 JOptionPane.WARNING_MESSAGE); 308 } 309 310 311 /** 312 * Creates a JEditorPane that displays a message. 313 * @param text the message of the editor pane in HTML format. 314 * @param font the font to be used in the message. 315 * @return a JEditorPane that displays a message. 316 */ 317 public static JEditorPane makeHtmlPane(CharSequence text, Font font) 318 { 319 JEditorPane pane = new JEditorPane(); 320 pane.setContentType("text/html"); 321 pane.setFont(font); 322 if (text != null) 323 { 324 pane.setText(applyFont(text, font)); 325 } 326 pane.setEditable(false); 327 pane.setBorder(new EmptyBorder(0, 0, 0, 0)); 328 pane.setOpaque(false); 329 pane.setFocusCycleRoot(false); 330 return pane; 331 } 332 333 /** 334 * Creates a JEditorPane that displays a message. 335 * @param text the message of the editor pane in plain text format. 336 * @param font the font to be used in the message. 337 * @return a JEditorPane that displays a message. 338 */ 339 public static JEditorPane makePlainTextPane(String text, Font font) 340 { 341 JEditorPane pane = new JEditorPane(); 342 pane.setContentType("text/plain"); 343 if (text != null) 344 { 345 pane.setText(text); 346 } 347 pane.setFont(font); 348 pane.setEditable(false); 349 pane.setBorder(new EmptyBorder(0, 0, 0, 0)); 350 pane.setOpaque(false); 351 pane.setFocusCycleRoot(false); 352 return pane; 353 } 354 355 /** 356 * Returns the HTML style representation for the given font. 357 * @param font the font for which we want to get an HTML style representation. 358 * @return the HTML style representation for the given font. 359 */ 360 private static String getFontStyle(Font font) 361 { 362 StringBuilder buf = new StringBuilder(); 363 364 buf.append("font-family:").append(font.getName()) 365 .append(";font-size:").append(font.getSize()).append("pt"); 366 367 if (font.isItalic()) 368 { 369 buf.append(";font-style:italic"); 370 } 371 372 if (font.isBold()) 373 { 374 buf.append(";font-weight:bold;"); 375 } 376 377 return buf.toString(); 378 } 379 380 /** 381 * Creates a titled border. 382 * @param msg the message to be displayed in the titled border. 383 * @return the created titled border. 384 */ 385 public static Border makeTitledBorder(LocalizableMessage msg) 386 { 387 TitledBorder border = new TitledBorder(new EtchedBorder(), 388 " "+msg+" "); 389 border.setTitleFont(ColorAndFontConstants.titleFont); 390 border.setTitleColor(ColorAndFontConstants.foreground); 391 return border; 392 } 393 394 /** 395 * Returns a JScrollPane that contains the provided component. The scroll 396 * pane will not contain any border. 397 * @param comp the component contained in the scroll pane. 398 * @return a JScrollPane that contains the provided component. The scroll 399 * pane will not contain any border. 400 */ 401 public static JScrollPane createBorderLessScrollBar(Component comp) 402 { 403 JScrollPane scroll = new JScrollPane(comp); 404 scroll.setBorder(new EmptyBorder(0, 0, 0, 0)); 405 scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); 406 scroll.setOpaque(false); 407 scroll.getViewport().setOpaque(false); 408 scroll.getViewport().setBackground(ColorAndFontConstants.background); 409 scroll.setBackground(ColorAndFontConstants.background); 410 UIFactory.setScrollIncrementUnit(scroll); 411 return scroll; 412 } 413 414 /** 415 * Returns a JScrollPane that contains the provided component. 416 * @param comp the component contained in the scroll pane. 417 * @return a JScrollPane that contains the provided component. 418 */ 419 public static JScrollPane createScrollPane(Component comp) 420 { 421 JScrollPane scroll = new JScrollPane(comp); 422 scroll.getViewport().setOpaque(false); 423 scroll.setOpaque(false); 424 scroll.getViewport().setBackground(ColorAndFontConstants.background); 425 scroll.setBackground(ColorAndFontConstants.background); 426 UIFactory.setScrollIncrementUnit(scroll); 427 return scroll; 428 } 429 430 /** 431 * Creates a button. 432 * @param text the message to be displayed by the button. 433 * @return the created button. 434 */ 435 public static JButton createButton(LocalizableMessage text) 436 { 437 JButton button = new JButton(text.toString()); 438 button.setOpaque(false); 439 button.setForeground(ColorAndFontConstants.buttonForeground); 440 button.getAccessibleContext().setAccessibleName(text.toString()); 441 return button; 442 } 443 444 /** 445 * Creates a radio button. 446 * @param text the message to be displayed by the radio button. 447 * @return the created radio button. 448 */ 449 public static JRadioButton createRadioButton(LocalizableMessage text) 450 { 451 JRadioButton button = new JRadioButton(text.toString()); 452 button.setOpaque(false); 453 button.setForeground(ColorAndFontConstants.buttonForeground); 454 button.getAccessibleContext().setAccessibleName(text.toString()); 455 return button; 456 } 457 458 /** 459 * Creates a check box. 460 * @param text the message to be displayed by the check box. 461 * @return the created check box. 462 */ 463 public static JCheckBox createCheckBox(LocalizableMessage text) 464 { 465 JCheckBox cb = new JCheckBox(text.toString()); 466 cb.setOpaque(false); 467 cb.setForeground(ColorAndFontConstants.buttonForeground); 468 cb.getAccessibleContext().setAccessibleName(text.toString()); 469 return cb; 470 } 471 472 /** 473 * Creates a menu item with the provided text. 474 * @param msg the text. 475 * @return a menu item with the provided text. 476 */ 477 public static JMenuItem createMenuItem(LocalizableMessage msg) 478 { 479 return new JMenuItem(msg.toString()); 480 } 481 482 /** 483 * Creates a menu with the provided text. 484 * @param msg the text. 485 * @param description the accessible description. 486 * @return a menu with the provided text. 487 */ 488 public static JMenu createMenu(LocalizableMessage msg, LocalizableMessage description) 489 { 490 JMenu menu = new JMenu(msg.toString()); 491 menu.getAccessibleContext().setAccessibleDescription( 492 description.toString()); 493 return menu; 494 } 495 496 /** 497 * Creates a label of type 'primary' (with bigger font than usual) with no 498 * text. 499 * @return the label of type 'primary' (with bigger font than usual) with no 500 * text. 501 */ 502 public static JLabel createPrimaryLabel() 503 { 504 return createPrimaryLabel(LocalizableMessage.EMPTY); 505 } 506 507 /** 508 * Creates a label of type 'primary' (with bigger font than usual). 509 * @param text the message to be displayed by the label. 510 * @return the label of type 'primary' (with bigger font than usual). 511 */ 512 public static JLabel createPrimaryLabel(LocalizableMessage text) 513 { 514 JLabel label = new JLabel(text.toString()); 515 label.setFont(ColorAndFontConstants.primaryFont); 516 label.setForeground(ColorAndFontConstants.foreground); 517 return label; 518 } 519 520 /** 521 * Creates a label of type 'inline help' (with smaller font). 522 * @param text the message to be displayed by the label. 523 * @return the label of type 'inline help' (with smaller font). 524 */ 525 public static JLabel createInlineHelpLabel(LocalizableMessage text) 526 { 527 JLabel label = new JLabel(text.toString()); 528 label.setFont(ColorAndFontConstants.inlineHelpFont); 529 label.setForeground(ColorAndFontConstants.foreground); 530 return label; 531 } 532 533 /** 534 * Creates a label of type 'title' (with bigger font). 535 * @param text the message to be displayed by the label. 536 * @return the label of type 'title' (with bigger font). 537 */ 538 public static JLabel createTitleLabel(LocalizableMessage text) 539 { 540 JLabel label = new JLabel(text.toString()); 541 label.setFont(ColorAndFontConstants.titleFont); 542 label.setForeground(ColorAndFontConstants.foreground); 543 return label; 544 } 545 546 /** 547 * Creates a label (with default font) with no text. 548 * @return the label (with default font) with no text. 549 */ 550 public static JLabel createDefaultLabel() 551 { 552 return createDefaultLabel(LocalizableMessage.EMPTY); 553 } 554 555 /** 556 * Creates a label (with default font). 557 * @param text the message to be displayed by the label. 558 * @return the label (with default font). 559 */ 560 public static JLabel createDefaultLabel(LocalizableMessage text) 561 { 562 JLabel label = new JLabel(text.toString()); 563 label.setFont(ColorAndFontConstants.defaultFont); 564 label.setForeground(ColorAndFontConstants.foreground); 565 return label; 566 } 567 568 /** 569 * Returns a table created with the provided model and renderers. 570 * @param tableModel the table model. 571 * @param renderer the cell renderer. 572 * @return a table created with the provided model and renderers. 573 */ 574 public static JTable createSortableTable(final SortableTableModel tableModel, 575 TableCellRenderer renderer) 576 { 577 final JTable table = new JTable(tableModel); 578 table.setShowGrid(true); 579 table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 580 table.setGridColor(ColorAndFontConstants.gridColor); 581 if (isMacOS()) 582 { 583 table.getTableHeader().setBorder( 584 BorderFactory.createMatteBorder(1, 1, 0, 0, 585 ColorAndFontConstants.gridColor)); 586 } 587 if (isWindows()) 588 { 589 table.getTableHeader().setBorder( 590 BorderFactory.createMatteBorder(1, 1, 0, 1, 591 ColorAndFontConstants.gridColor)); 592 } 593 table.getTableHeader().setDefaultRenderer( 594 new AccessibleTableHeaderRenderer( 595 table.getTableHeader().getDefaultRenderer())); 596 597 for (int i=0; i<tableModel.getColumnCount(); i++) 598 { 599 TableColumn col = table.getColumn(table.getColumnName(i)); 600 col.setCellRenderer(renderer); 601 } 602 MouseAdapter listMouseListener = new MouseAdapter() { 603 @Override 604 public void mouseClicked(MouseEvent e) { 605 TableColumnModel columnModel = table.getColumnModel(); 606 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 607 int sortedBy = table.convertColumnIndexToModel(viewColumn); 608 if (e.getClickCount() == 1 && sortedBy != -1) { 609 tableModel.setSortAscending(!tableModel.isSortAscending()); 610 tableModel.setSortColumn(sortedBy); 611 tableModel.forceResort(); 612 updateTableSizes(table); 613 } 614 } 615 }; 616 table.getTableHeader().addMouseListener(listMouseListener); 617 return table; 618 } 619 620 /** 621 * Creates a text area with borders similar to the ones of a text field. 622 * @param text the text of the text area. 623 * @param rows the rows of the text area. 624 * @param cols the columns of the text area. 625 * @return a text area with borders similar to the ones of a text field. 626 */ 627 public static JTextArea createTextAreaWithBorder(LocalizableMessage text, int rows, 628 int cols) 629 { 630 JTextArea ta = createTextArea(text, rows, cols); 631 if (ColorAndFontConstants.textAreaBorder != null) 632 { 633 setBorder(ta, ColorAndFontConstants.textAreaBorder); 634 } 635 return ta; 636 } 637 638 /** 639 * Creates a non-editable text area. 640 * @param text the text of the text area. 641 * @param rows the rows of the text area. 642 * @param cols the columns of the text area. 643 * @return a non-editable text area. 644 */ 645 public static JTextArea createNonEditableTextArea(LocalizableMessage text, int rows, 646 int cols) 647 { 648 JTextArea ta = createTextArea(text, rows, cols); 649 ta.setEditable(false); 650 ta.setOpaque(false); 651 ta.setForeground(ColorAndFontConstants.foreground); 652 return ta; 653 } 654 655 /** 656 * Creates a text area. 657 * @param text the text of the text area. 658 * @param rows the rows of the text area. 659 * @param cols the columns of the text area. 660 * @return a text area. 661 */ 662 public static JTextArea createTextArea(LocalizableMessage text, int rows, 663 int cols) 664 { 665 JTextArea ta = new JTextArea(text.toString(), rows, cols); 666 ta.setFont(ColorAndFontConstants.defaultFont); 667 return ta; 668 } 669 670 /** 671 * Creates a text field. 672 * @param text the text of the text field. 673 * @param cols the columns of the text field. 674 * @return the created text field. 675 */ 676 public static JTextField createTextField(String text, int cols) 677 { 678 JTextField tf = createTextField(); 679 tf.setText(text); 680 tf.setColumns(cols); 681 return tf; 682 } 683 684 /** 685 * Creates a short text field. 686 * @return the created text field. 687 */ 688 public static JTextField createShortTextField() 689 { 690 JTextField tf = createTextField(); 691 tf.setColumns(10); 692 return tf; 693 } 694 695 /** 696 * Creates a medium sized text field. 697 * @return the created text field. 698 */ 699 public static JTextField createMediumTextField() 700 { 701 JTextField tf = createTextField(); 702 tf.setColumns(20); 703 return tf; 704 } 705 706 /** 707 * Creates a long text field. 708 * @return the created text field. 709 */ 710 public static JTextField createLongTextField() 711 { 712 JTextField tf = createTextField(); 713 tf.setColumns(30); 714 return tf; 715 } 716 717 718 /** 719 * Creates a text field with the default size. 720 * @return the created text field. 721 */ 722 public static JTextField createTextField() 723 { 724 JTextField tf = new JTextField(); 725 tf.addFocusListener(new TextComponentFocusListener(tf)); 726 tf.setFont(ColorAndFontConstants.defaultFont); 727 return tf; 728 } 729 730 /** 731 * Creates a pasword text field. 732 * @return the created password text field. 733 */ 734 public static JPasswordField createPasswordField() 735 { 736 JPasswordField pf = new JPasswordField(); 737 pf.addFocusListener(new TextComponentFocusListener(pf)); 738 pf.setFont(ColorAndFontConstants.defaultFont); 739 return pf; 740 } 741 742 /** 743 * Creates a pasword text field. 744 * @param cols the columns of the password text field. 745 * @return the created password text field. 746 */ 747 public static JPasswordField createPasswordField(int cols) 748 { 749 JPasswordField pf = createPasswordField(); 750 pf.setColumns(cols); 751 return pf; 752 } 753 754 755 /** 756 * Sets the border in a given component. If the component already has a 757 * border, creates a compound border. 758 * @param comp the component. 759 * @param border the border to be set. 760 */ 761 public static void setBorder(JComponent comp, Border border) 762 { 763 if (comp.getBorder() != null) 764 { 765 comp.setBorder(BorderFactory.createCompoundBorder(comp.getBorder(), border)); 766 } 767 else 768 { 769 comp.setBorder(border); 770 } 771 } 772 773 /** 774 * Checks the size of the table and of the scroll bar where it is contained, 775 * and depending on it updates the auto resize mode. 776 * @param scroll the scroll pane containing the table. 777 * @param table the table. 778 */ 779 public static void updateScrollMode(JScrollPane scroll, JTable table) 780 { 781 int width1 = table.getPreferredScrollableViewportSize().width; 782 int width2 = scroll.getViewport().getWidth(); 783 784 if (width1 > width2) 785 { 786 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 787 } 788 else 789 { 790 table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); 791 } 792 } 793 794 /** 795 * Updates the size of the table rows according to the size of the 796 * rendered component. 797 * @param table the table to handle. 798 */ 799 public static void updateTableSizes(JTable table) 800 { 801 updateTableSizes(table, -1); 802 } 803 804 /** 805 * Updates the size of the table rows according to the size of the 806 * rendered component. 807 * @param table the table to handle. 808 * @param rows the maximum rows to be displayed (-1 for unlimited) 809 */ 810 public static void updateTableSizes(JTable table, int rows) 811 { 812 int horizontalMargin = table.getIntercellSpacing().width; 813 int verticalMargin = table.getIntercellSpacing().height; 814 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 815 816 int headerMaxHeight = 5; 817 int headerMaxWidth = 0; 818 819 JTableHeader header = table.getTableHeader(); 820 if (header != null && header.isVisible()) 821 { 822 for (int col=0; col<table.getColumnCount(); col++) 823 { 824 TableColumn tcol = table.getColumnModel().getColumn(col); 825 TableCellRenderer renderer = tcol.getHeaderRenderer(); 826 if (renderer == null) 827 { 828 renderer = table.getTableHeader().getDefaultRenderer(); 829 } 830 Component comp = renderer.getTableCellRendererComponent(table, 831 table.getModel().getColumnName(col), false, false, 0, col); 832 int colHeight = comp.getPreferredSize().height + 2 * verticalMargin; 833 if (colHeight > screenSize.height) 834 { 835 // There are some issues on Mac OS and sometimes the preferred size 836 // is too big. 837 colHeight = 0; 838 } 839 headerMaxHeight = Math.max(headerMaxHeight, colHeight); 840 } 841 } 842 843 for (int col=0; col<table.getColumnCount(); col++) 844 { 845 int colMaxWidth = 8; 846 TableColumn tcol = table.getColumnModel().getColumn(col); 847 TableCellRenderer renderer = tcol.getHeaderRenderer(); 848 849 if (renderer == null && header != null) 850 { 851 renderer = header.getDefaultRenderer(); 852 } 853 854 if (renderer != null) 855 { 856 Component comp = renderer.getTableCellRendererComponent(table, 857 table.getModel().getColumnName(col), false, false, 0, col); 858 colMaxWidth = comp.getPreferredSize().width + 2 * horizontalMargin + 8; 859 } 860 861 if (colMaxWidth > screenSize.width) 862 { 863 colMaxWidth = 8; 864 } 865 866 for (int row=0; row<table.getRowCount(); row++) 867 { 868 renderer = table.getCellRenderer(row, col); 869 Component comp = table.prepareRenderer(renderer, row, col); 870 int colWidth = comp.getPreferredSize().width + 2 * horizontalMargin; 871 colMaxWidth = Math.max(colMaxWidth, colWidth); 872 } 873 tcol.setPreferredWidth(colMaxWidth); 874 headerMaxWidth += colMaxWidth; 875 } 876 877 878 if (header != null && header.isVisible()) 879 { 880 header.setPreferredSize(new Dimension(headerMaxWidth, headerMaxHeight)); 881 } 882 883 884 int maxRow = table.getRowHeight(); 885 for (int row=0; row<table.getRowCount(); row++) 886 { 887 for (int col=0; col<table.getColumnCount(); col++) 888 { 889 TableCellRenderer renderer = table.getCellRenderer(row, col); 890 Component comp = renderer.getTableCellRendererComponent(table, 891 table.getModel().getValueAt(row, col), false, false, row, col); 892 int colHeight = comp.getPreferredSize().height + 2 * verticalMargin; 893 if (colHeight > screenSize.height) 894 { 895 colHeight = 0; 896 } 897 maxRow = Math.max(maxRow, colHeight); 898 } 899 } 900 if (maxRow > table.getRowHeight()) 901 { 902 table.setRowHeight(maxRow); 903 } 904 Dimension d1; 905 if (rows == -1) 906 { 907 d1 = table.getPreferredSize(); 908 } 909 else 910 { 911 d1 = new Dimension(table.getPreferredSize().width, rows * maxRow); 912 } 913 table.setPreferredScrollableViewportSize(d1); 914 } 915 916 /** 917 * Returns a String that contains the html passed as parameter with a span 918 * applied. The span style corresponds to the Font specified as parameter. 919 * The goal of this method is to be able to specify a font for an HTML string. 920 * 921 * @param html the original html text. 922 * @param font the font to be used to generate the new HTML. 923 * @return a string that represents the original HTML with the font specified 924 * as parameter. 925 */ 926 public static String applyFont(CharSequence html, Font font) 927 { 928 return "<span style=\"" + getFontStyle(font) + "\">" + html + "</span>"; 929 } 930 931 932 /** 933 * Returns an ImageIcon or <CODE>null</CODE> if the path was invalid. 934 * @param path the path of the image. 935 * @param loader the class loader to use to load the image. If 936 * <CODE>null</CODE> this class class loader will be used. 937 * @return an ImageIcon or <CODE>null</CODE> if the path was invalid. 938 */ 939 public static ImageIcon createImageIcon(String path, ClassLoader loader) { 940 if (loader == null) 941 { 942 loader = ControlPanel.class.getClassLoader(); 943 } 944 java.net.URL imgURL = loader.getResource(path); 945 return imgURL != null ? new ImageIcon(imgURL) : null; 946 } 947 948 /** 949 * Returns an ImageIcon or <CODE>null</CODE> if the path was invalid. 950 * @param path the path of the image. 951 * @return an ImageIcon or <CODE>null</CODE> if the path was invalid. 952 */ 953 public static ImageIcon createImageIcon(String path) { 954 return createImageIcon(path, null); 955 } 956 957 /** 958 * Creates an image icon using an array of bytes that contain the image and 959 * specifying the maximum height of the image. 960 * @param bytes the byte array. 961 * @param maxHeight the maximum height of the image. 962 * @param description the description of the image. 963 * @param useFast whether a fast algorithm must be used to transform the image 964 * or an algorithm with a better result. 965 * @return an image icon using an array of bytes that contain the image and 966 * specifying the maximum height of the image. 967 */ 968 public static ImageIcon createImageIcon(byte[] bytes, int maxHeight, 969 LocalizableMessage description, boolean useFast) 970 { 971 ImageIcon icon = new ImageIcon(bytes, description.toString()); 972 if (maxHeight > icon.getIconHeight() || icon.getIconHeight() <= 0) 973 { 974 return icon; 975 } 976 int newHeight = maxHeight; 977 int newWidth = (newHeight * icon.getIconWidth()) / icon.getIconHeight(); 978 int algo = useFast ? Image.SCALE_FAST : Image.SCALE_SMOOTH; 979 Image scaledImage = icon.getImage().getScaledInstance(newWidth, newHeight, algo); 980 return new ImageIcon(scaledImage); 981 } 982 983 /** 984 * Updates the preferred size of an editor pane. 985 * @param pane the panel to be updated. 986 * @param nCols the number of columns that the panel must have. 987 * @param plainText the text to be displayed (plain text). 988 * @param font the font to be used. 989 * @param applyBackground whether an error/warning background must be applied 990 * to the text or not. 991 */ 992 public static void updatePreferredSize(JEditorPane pane, int nCols, 993 String plainText, Font font, boolean applyBackground) 994 { 995 String wrappedText = wrapText(plainText, nCols); 996 wrappedText = wrappedText.replaceAll(ServerConstants.EOL, "<br>"); 997 if (applyBackground) 998 { 999 wrappedText = UIFactory.applyErrorBackgroundToHtml( 1000 Utilities.applyFont(wrappedText, font)); 1001 } 1002 JEditorPane pane2 = makeHtmlPane(wrappedText, font); 1003 pane.setPreferredSize(pane2.getPreferredSize()); 1004 JFrame frame = getFrame(pane); 1005 if (frame != null && frame.isVisible()) 1006 { 1007 frame.getRootPane().revalidate(); 1008 frame.getRootPane().repaint(); 1009 } 1010 } 1011 1012 /** 1013 * Strips any potential HTML markup from a given string. 1014 * @param s string to strip 1015 * @return resulting string 1016 */ 1017 public static String stripHtmlToSingleLine(String s) { 1018 String o = null; 1019 if (s != null) { 1020 s = s.replaceAll("<br>", " "); 1021 // This is not a comprehensive solution but addresses 1022 // the few tags that we have in Resources.properties 1023 // at the moment. Note that the following might strip 1024 // out more than is intended for non-tags like 1025 // '<your name here>' or for funky tags like 1026 // '<tag attr="1 > 0">'. See test class for cases that 1027 // might cause problems. 1028 o = s.replaceAll("\\<.*?\\>",""); 1029 } 1030 return o; 1031 } 1032 1033 /** 1034 * Wraps the contents of the provided message using the specified number of 1035 * columns. 1036 * @param msg the message to be wrapped. 1037 * @param nCols the number of columns. 1038 * @return the wrapped message. 1039 */ 1040 public static LocalizableMessage wrapHTML(LocalizableMessage msg, int nCols) 1041 { 1042 String s = msg.toString(); 1043 StringBuilder sb = new StringBuilder(); 1044 StringBuilder lastLine = new StringBuilder(); 1045 int lastOpenTag = -1; 1046 boolean inTag = false; 1047 int lastSpace = -1; 1048 int lastLineLengthInLastSpace = 0; 1049 int lastLineLength = 0; 1050 for (int i=0; i<s.length() ; i++) 1051 { 1052 boolean isNormalChar = false; 1053 char c = s.charAt(i); 1054 if (c == '<') 1055 { 1056 inTag = true; 1057 lastOpenTag = i; 1058 lastLine.append(c); 1059 } 1060 else if (c == '>') 1061 { 1062 if (lastOpenTag != -1) 1063 { 1064 inTag = false; 1065 String tag = s.substring(lastOpenTag, i+1); 1066 lastOpenTag = -1; 1067 lastLine.append(c); 1068 if (isLineBreakTag(tag)) 1069 { 1070 sb.append(lastLine); 1071 lastLine.delete(0, lastLine.length()); 1072 lastLineLength = 0; 1073 lastSpace = -1; 1074 lastLineLengthInLastSpace = 0; 1075 } 1076 } 1077 else 1078 { 1079 isNormalChar = true; 1080 } 1081 } 1082 else if (inTag) 1083 { 1084 lastLine.append(c); 1085 } 1086 else if (c == HTML_SPACE.charAt(0)) 1087 { 1088 if (s.length() >= i + HTML_SPACE.length()) 1089 { 1090 if (HTML_SPACE.equalsIgnoreCase(s.substring(i, i 1091 + HTML_SPACE.length()))) 1092 { 1093 if (lastLineLength < nCols) 1094 { 1095 // Only count as 1 space 1096 lastLine.append(HTML_SPACE); 1097 lastSpace = lastLine.length() - HTML_SPACE.length(); 1098 lastLineLength ++; 1099 lastLineLengthInLastSpace = lastLineLength; 1100 i += HTML_SPACE.length() - 1; 1101 } 1102 else 1103 { 1104 // Insert a line break 1105 sb.append(lastLine); 1106 sb.append("<br>"); 1107 lastLine.delete(0, lastLine.length()); 1108 lastLineLength = 0; 1109 lastSpace = -1; 1110 lastLineLengthInLastSpace = 0; 1111 i += HTML_SPACE.length() - 1; 1112 } 1113 } 1114 else 1115 { 1116 isNormalChar = true; 1117 } 1118 } 1119 else 1120 { 1121 isNormalChar = true; 1122 } 1123 } 1124 else if (c == ' ') 1125 { 1126 if (lastLineLength < nCols) 1127 { 1128 // Only count as 1 space 1129 lastLine.append(c); 1130 lastSpace = lastLine.length() - 1; 1131 lastLineLength ++; 1132 lastLineLengthInLastSpace = lastLineLength; 1133 } 1134 else 1135 { 1136 // Insert a line break 1137 sb.append(lastLine); 1138 sb.append("<br>"); 1139 lastLine.delete(0, lastLine.length()); 1140 lastLineLength = 0; 1141 lastSpace = -1; 1142 lastLineLengthInLastSpace = 0; 1143 } 1144 } 1145 else 1146 { 1147 isNormalChar = true; 1148 } 1149 1150 if (isNormalChar) 1151 { 1152 if (lastLineLength < nCols) 1153 { 1154 lastLine.append(c); 1155 lastLineLength ++; 1156 } 1157 else 1158 { 1159 // Check where to insert a line break 1160 if (lastSpace != -1) 1161 { 1162 sb.append(lastLine, 0, lastSpace); 1163 sb.append("<br>"); 1164 lastLine.delete(0, lastSpace + 1); 1165 lastLine.append(c); 1166 lastLineLength = lastLineLength - lastLineLengthInLastSpace + 1; 1167 lastLineLengthInLastSpace = 0; 1168 lastSpace = -1; 1169 } 1170 else 1171 { 1172 // Force the line break. 1173 sb.append(lastLine); 1174 sb.append("<br>"); 1175 lastLine.delete(0, lastLine.length()); 1176 lastLine.append(c); 1177 lastLineLength = 1; 1178 } 1179 } 1180 } 1181 } 1182 if (lastLine.length() > 0) 1183 { 1184 sb.append(lastLine); 1185 } 1186 return LocalizableMessage.raw(sb.toString()); 1187 } 1188 1189 private static boolean isLineBreakTag(String tag) 1190 { 1191 return "<br>".equalsIgnoreCase(tag) || 1192 "</br>".equalsIgnoreCase(tag) || 1193 "</div>".equalsIgnoreCase(tag) || 1194 "<p>".equalsIgnoreCase(tag) || 1195 "</p>".equalsIgnoreCase(tag); 1196 } 1197 1198 /** 1199 * Center the component location based on its preferred size. The code 1200 * considers the particular case of 2 screens and puts the component on the 1201 * center of the left screen 1202 * 1203 * @param comp the component to be centered. 1204 */ 1205 public static void centerOnScreen(Component comp) 1206 { 1207 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 1208 1209 int width = comp.getPreferredSize().width; 1210 int height = comp.getPreferredSize().height; 1211 1212 boolean multipleScreen = screenSize.width / screenSize.height >= 2; 1213 1214 if (multipleScreen) 1215 { 1216 comp.setLocation(screenSize.width / 4 - width / 2, 1217 (screenSize.height - height) / 2); 1218 } else 1219 { 1220 comp.setLocation((screenSize.width - width) / 2, 1221 (screenSize.height - height) / 2); 1222 } 1223 } 1224 1225 /** 1226 * Center the component location of the ref component. 1227 * 1228 * @param comp the component to be centered. 1229 * @param ref the component to be used as reference. 1230 * 1231 */ 1232 public static void centerGoldenMean(Window comp, Component ref) 1233 { 1234 comp.setLocationRelativeTo(ref); 1235 // Apply the golden mean 1236 if (ref != null && ref.isVisible()) 1237 { 1238 int refY = ref.getY(); 1239 int refHeight = ref.getHeight(); 1240 int compHeight = comp.getPreferredSize().height; 1241 1242 int newY = refY + (int) (refHeight * 0.3819 - compHeight * 0.5); 1243 // Check that the new window will be fully visible 1244 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 1245 if (newY > 0 && screenSize.height > newY + compHeight) 1246 { 1247 comp.setLocation(comp.getX(), newY); 1248 } 1249 } 1250 } 1251 1252 /** 1253 * Returns the parent frame of a component. <CODE>null</CODE> if this 1254 * component is not contained in any frame. 1255 * @param comp the component. 1256 * @return the parent frame of a component. <CODE>null</CODE> if this 1257 * component is not contained in any frame. 1258 */ 1259 public static JFrame getFrame(Component comp) 1260 { 1261 Component parent = comp; 1262 while (parent != null && !(parent instanceof JFrame)) 1263 { 1264 parent = parent.getParent(); 1265 } 1266 return parent != null ? (JFrame) parent : null; 1267 } 1268 1269 /** 1270 * Returns the parent dialog of a component. <CODE>null</CODE> if this 1271 * component is not contained in any dialog. 1272 * @param comp the component. 1273 * @return the parent dialog of a component. <CODE>null</CODE> if this 1274 * component is not contained in any dialog. 1275 */ 1276 public static Window getParentDialog(Component comp) 1277 { 1278 Component parent = comp; 1279 while (parent != null) 1280 { 1281 if (parent instanceof JDialog || parent instanceof JFrame) 1282 { 1283 return (Window)parent; 1284 } 1285 parent = parent.getParent(); 1286 } 1287 return null; 1288 } 1289 1290 /** 1291 * Unescapes UTF-8 text and generates a String from it. 1292 * @param v the string in UTF-8 format. 1293 * @return the string with unescaped characters. 1294 */ 1295 public static String unescapeUtf8(String v) 1296 { 1297 try 1298 { 1299 byte[] stringBytes = v.getBytes("UTF-8"); 1300 byte[] decodedBytes = new byte[stringBytes.length]; 1301 int pos = 0; 1302 for (int i = 0; i < stringBytes.length; i++) 1303 { 1304 if (stringBytes[i] == '\\' 1305 && i + 2 < stringBytes.length 1306 && StaticUtils.isHexDigit(stringBytes[i+1]) 1307 && StaticUtils.isHexDigit(stringBytes[i+2])) 1308 { 1309 // Convert hex-encoded UTF-8 to 16-bit chars. 1310 byte b; 1311 1312 byte escapedByte1 = stringBytes[++i]; 1313 switch (escapedByte1) 1314 { 1315 case '0': 1316 b = (byte) 0x00; 1317 break; 1318 case '1': 1319 b = (byte) 0x10; 1320 break; 1321 case '2': 1322 b = (byte) 0x20; 1323 break; 1324 case '3': 1325 b = (byte) 0x30; 1326 break; 1327 case '4': 1328 b = (byte) 0x40; 1329 break; 1330 case '5': 1331 b = (byte) 0x50; 1332 break; 1333 case '6': 1334 b = (byte) 0x60; 1335 break; 1336 case '7': 1337 b = (byte) 0x70; 1338 break; 1339 case '8': 1340 b = (byte) 0x80; 1341 break; 1342 case '9': 1343 b = (byte) 0x90; 1344 break; 1345 case 'a': 1346 case 'A': 1347 b = (byte) 0xA0; 1348 break; 1349 case 'b': 1350 case 'B': 1351 b = (byte) 0xB0; 1352 break; 1353 case 'c': 1354 case 'C': 1355 b = (byte) 0xC0; 1356 break; 1357 case 'd': 1358 case 'D': 1359 b = (byte) 0xD0; 1360 break; 1361 case 'e': 1362 case 'E': 1363 b = (byte) 0xE0; 1364 break; 1365 case 'f': 1366 case 'F': 1367 b = (byte) 0xF0; 1368 break; 1369 default: 1370 throw new RuntimeException("Unexpected byte: "+escapedByte1); 1371 } 1372 1373 byte escapedByte2 = stringBytes[++i]; 1374 switch (escapedByte2) 1375 { 1376 case '0': 1377 break; 1378 case '1': 1379 b |= 0x01; 1380 break; 1381 case '2': 1382 b |= 0x02; 1383 break; 1384 case '3': 1385 b |= 0x03; 1386 break; 1387 case '4': 1388 b |= 0x04; 1389 break; 1390 case '5': 1391 b |= 0x05; 1392 break; 1393 case '6': 1394 b |= 0x06; 1395 break; 1396 case '7': 1397 b |= 0x07; 1398 break; 1399 case '8': 1400 b |= 0x08; 1401 break; 1402 case '9': 1403 b |= 0x09; 1404 break; 1405 case 'a': 1406 case 'A': 1407 b |= 0x0A; 1408 break; 1409 case 'b': 1410 case 'B': 1411 b |= 0x0B; 1412 break; 1413 case 'c': 1414 case 'C': 1415 b |= 0x0C; 1416 break; 1417 case 'd': 1418 case 'D': 1419 b |= 0x0D; 1420 break; 1421 case 'e': 1422 case 'E': 1423 b |= 0x0E; 1424 break; 1425 case 'f': 1426 case 'F': 1427 b |= 0x0F; 1428 break; 1429 default: 1430 throw new RuntimeException("Unexpected byte: "+escapedByte2); 1431 } 1432 1433 decodedBytes[pos++] = b; 1434 } 1435 else { 1436 decodedBytes[pos++] = stringBytes[i]; 1437 } 1438 } 1439 return new String(decodedBytes, 0, pos, "UTF-8"); 1440 } 1441 catch (UnsupportedEncodingException uee) 1442 { 1443// This is a bug, UTF-8 should be supported always by the JVM 1444 throw new RuntimeException("UTF-8 encoding not supported", uee); 1445 } 1446 } 1447 1448 /** 1449 * Returns <CODE>true</CODE> if the the provided strings represent the same 1450 * DN and <CODE>false</CODE> otherwise. 1451 * @param dn1 the first dn to compare. 1452 * @param dn2 the second dn to compare. 1453 * @return <CODE>true</CODE> if the the provided strings represent the same 1454 * DN and <CODE>false</CODE> otherwise. 1455 */ 1456 public static boolean areDnsEqual(String dn1, String dn2) 1457 { 1458 try 1459 { 1460 LdapName name1 = new LdapName(dn1); 1461 LdapName name2 = new LdapName(dn2); 1462 return name1.equals(name2); 1463 } catch (Exception ex) 1464 { 1465 return false; 1466 } 1467 } 1468 1469 /** 1470 * Returns the attribute name with no options (or subtypes). 1471 * @param attrName the complete attribute name. 1472 * @return the attribute name with no options (or subtypes). 1473 */ 1474 public static String getAttributeNameWithoutOptions(String attrName) 1475 { 1476 int index = attrName.indexOf(";"); 1477 if (index != -1) 1478 { 1479 attrName = attrName.substring(0, index); 1480 } 1481 return attrName; 1482 } 1483 1484 /** 1485 * Strings any potential "separator" from a given string. 1486 * @param s string to strip 1487 * @param separator the separator string to remove 1488 * @return resulting string 1489 */ 1490 private static String stripStringToSingleLine(String s, String separator) 1491 { 1492 if (s != null) 1493 { 1494 return s.replaceAll(separator, ""); 1495 } 1496 return null; 1497 } 1498 1499 /** The pattern for control characters. */ 1500 private final static Pattern cntrl_pattern = Pattern.compile("\\p{Cntrl}", Pattern.MULTILINE); 1501 1502 /** 1503 * Checks if a string contains control characters. 1504 * @param s : the string to check 1505 * @return true if s contains control characters, false otherwise 1506 */ 1507 public static boolean hasControlCharaters(String s) 1508 { 1509 return cntrl_pattern.matcher(s).find(); 1510 } 1511 1512 /** 1513 * This is a helper method that gets a String representation of the elements 1514 * in the Collection. The String will display the different elements separated 1515 * by the separator String. 1516 * 1517 * @param col 1518 * the collection containing the String. 1519 * @param separator 1520 * the separator String to be used. 1521 * @return the String representation for the collection. 1522 */ 1523 public static String getStringFromCollection(Collection<String> col, String separator) 1524 { 1525 StringBuilder msg = new StringBuilder(); 1526 for (String m : col) 1527 { 1528 if (msg.length() > 0) 1529 { 1530 msg.append(separator); 1531 } 1532 msg.append(stripStringToSingleLine(m, separator)); 1533 } 1534 return msg.toString(); 1535 } 1536 1537 /** 1538 * Commodity method to get the Name object representing a dn. 1539 * It is preferable to use Name objects when doing JNDI operations to avoid 1540 * problems with the '/' character. 1541 * @param dn the DN as a String. 1542 * @return a Name object representing the DN. 1543 * @throws InvalidNameException if the provided DN value is not valid. 1544 * 1545 */ 1546 public static Name getJNDIName(String dn) throws InvalidNameException 1547 { 1548 Name name = new CompositeName(); 1549 if (dn != null && dn.length() > 0) { 1550 name.add(dn); 1551 } 1552 return name; 1553 } 1554 1555 /** 1556 * Returns the HTML representation of the 'Done' string. 1557 * @param progressFont the font to be used. 1558 * @return the HTML representation of the 'Done' string. 1559 */ 1560 public static String getProgressDone(Font progressFont) 1561 { 1562 return applyFont(INFO_CTRL_PANEL_PROGRESS_DONE.get(), 1563 progressFont.deriveFont(Font.BOLD)); 1564 } 1565 1566 /** 1567 * Returns the HTML representation of a message to which some points have 1568 * been appended. 1569 * @param plainText the plain text. 1570 * @param progressFont the font to be used. 1571 * @return the HTML representation of a message to which some points have 1572 * been appended. 1573 */ 1574 public static String getProgressWithPoints(LocalizableMessage plainText, 1575 Font progressFont) 1576 { 1577 return applyFont(plainText.toString(), progressFont)+ 1578 applyFont(" ..... ", 1579 progressFont.deriveFont(Font.BOLD)); 1580 } 1581 1582 /** 1583 * Returns the HTML representation of an error for a given text. 1584 * @param title the title. 1585 * @param titleFont the font for the title. 1586 * @param details the details. 1587 * @param detailsFont the font to be used for the details. 1588 * @return the HTML representation of an error for the given text. 1589 */ 1590 public static String getFormattedError(LocalizableMessage title, Font titleFont, 1591 LocalizableMessage details, Font detailsFont) 1592 { 1593 StringBuilder buf = new StringBuilder(); 1594 buf.append(UIFactory.getIconHtml(UIFactory.IconType.ERROR_LARGE)) 1595 .append(HTML_SPACE).append(HTML_SPACE) 1596 .append(applyFont(title.toString(), titleFont)); 1597 if (details != null) 1598 { 1599 buf.append("<br><br>") 1600 .append(applyFont(details.toString(), detailsFont)); 1601 } 1602 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1603 "</form>"; 1604 } 1605 1606 /** 1607 * Returns the HTML representation of a success for a given text. 1608 * @param title the title. 1609 * @param titleFont the font for the title. 1610 * @param details the details. 1611 * @param detailsFont the font to be used for the details. 1612 * @return the HTML representation of a success for the given text. 1613 */ 1614 public static String getFormattedSuccess(LocalizableMessage title, Font titleFont, 1615 LocalizableMessage details, Font detailsFont) 1616 { 1617 StringBuilder buf = new StringBuilder(); 1618 buf.append(UIFactory.getIconHtml(UIFactory.IconType.INFORMATION_LARGE)) 1619 .append(HTML_SPACE).append(HTML_SPACE) 1620 .append(applyFont(title.toString(), titleFont)); 1621 if (details != null) 1622 { 1623 buf.append("<br><br>") 1624 .append(applyFont(details.toString(), detailsFont)); 1625 } 1626 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1627 "</form>"; 1628 } 1629 1630 /** 1631 * Returns the HTML representation of a confirmation for a given text. 1632 * @param title the title. 1633 * @param titleFont the font for the title. 1634 * @param details the details. 1635 * @param detailsFont the font to be used for the details. 1636 * @return the HTML representation of a confirmation for the given text. 1637 */ 1638 public static String getFormattedConfirmation(LocalizableMessage title, Font titleFont, 1639 LocalizableMessage details, Font detailsFont) 1640 { 1641 StringBuilder buf = new StringBuilder(); 1642 buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) 1643 .append(HTML_SPACE).append(HTML_SPACE) 1644 .append(applyFont(title.toString(), titleFont)); 1645 if (details != null) 1646 { 1647 buf.append("<br><br>") 1648 .append(applyFont(details.toString(), detailsFont)); 1649 } 1650 return "<form>" + buf + "</form>"; 1651 } 1652 1653 1654 /** 1655 * Returns the HTML representation of a warning for a given text. 1656 * @param title the title. 1657 * @param titleFont the font for the title. 1658 * @param details the details. 1659 * @param detailsFont the font to be used for the details. 1660 * @return the HTML representation of a success for the given text. 1661 */ 1662 public static String getFormattedWarning(LocalizableMessage title, Font titleFont, 1663 LocalizableMessage details, Font detailsFont) 1664 { 1665 StringBuilder buf = new StringBuilder(); 1666 buf.append(UIFactory.getIconHtml(UIFactory.IconType.WARNING_LARGE)) 1667 .append(HTML_SPACE).append(HTML_SPACE) 1668 .append(applyFont(title.toString(), titleFont)); 1669 if (details != null) 1670 { 1671 buf.append("<br><br>") 1672 .append(applyFont(details.toString(), detailsFont)); 1673 } 1674 return "<form>"+UIFactory.applyErrorBackgroundToHtml(buf.toString())+ 1675 "</form>"; 1676 } 1677 1678 /** 1679 * Sets the not available text to a label and associates a help icon and 1680 * a tooltip explaining that the data is not available because the server is 1681 * down. 1682 * @param l the label. 1683 */ 1684 public static void setNotAvailableBecauseServerIsDown(LabelWithHelpIcon l) 1685 { 1686 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1687 l.setHelpIconVisible(true); 1688 l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); 1689 } 1690 1691 /** 1692 * Sets the not available text to a label and associates a help icon and 1693 * a tooltip explaining that the data is not available because authentication 1694 * is required. 1695 * @param l the label. 1696 */ 1697 public static void setNotAvailableBecauseAuthenticationIsRequired( 1698 LabelWithHelpIcon l) 1699 { 1700 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1701 l.setHelpIconVisible(true); 1702 l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); 1703 } 1704 1705 /** 1706 * Sets the not available text to a label and associates a help icon and 1707 * a tooltip explaining that the data is not available because the server is 1708 * down. 1709 * @param l the label. 1710 */ 1711 public static void setNotAvailableBecauseServerIsDown( 1712 SelectableLabelWithHelpIcon l) 1713 { 1714 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1715 l.setHelpIconVisible(true); 1716 l.setHelpTooltip(INFO_NOT_AVAILABLE_SERVER_DOWN_TOOLTIP.get().toString()); 1717 } 1718 1719 /** 1720 * Sets the not available text to a label and associates a help icon and 1721 * a tooltip explaining that the data is not available because authentication 1722 * is required. 1723 * @param l the label. 1724 */ 1725 public static void setNotAvailableBecauseAuthenticationIsRequired( 1726 SelectableLabelWithHelpIcon l) 1727 { 1728 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1729 l.setHelpIconVisible(true); 1730 l.setHelpTooltip(INFO_NOT_AVAILABLE_AUTHENTICATION_REQUIRED_TOOLTIP.get().toString()); 1731 } 1732 1733 /** 1734 * Updates a label by setting a warning icon and a text. 1735 * @param l the label to be updated. 1736 * @param text the text to be set on the label. 1737 */ 1738 public static void setWarningLabel(JLabel l, LocalizableMessage text) 1739 { 1740 l.setText(text.toString()); 1741 if (warningIcon == null) 1742 { 1743 warningIcon = 1744 createImageIcon("org/opends/quicksetup/images/warning_medium.gif"); 1745 warningIcon.setDescription( 1746 INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 1747 warningIcon.getAccessibleContext().setAccessibleName( 1748 INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 1749 } 1750 l.setIcon(warningIcon); 1751 l.setToolTipText(text.toString()); 1752 l.setHorizontalTextPosition(SwingConstants.RIGHT); 1753 } 1754 1755 /** 1756 * Sets the not available text to a label with no icon nor tooltip. 1757 * @param l the label. 1758 */ 1759 public static void setNotAvailable(LabelWithHelpIcon l) 1760 { 1761 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1762 l.setHelpIconVisible(false); 1763 l.setHelpTooltip(null); 1764 } 1765 1766 /** 1767 * Sets the a text to a label with no icon nor tooltip. 1768 * @param l the label. 1769 * @param text the text. 1770 */ 1771 public static void setTextValue(LabelWithHelpIcon l, String text) 1772 { 1773 l.setText(text); 1774 l.setHelpIconVisible(false); 1775 l.setHelpTooltip(null); 1776 } 1777 1778 /** 1779 * Sets the not available text to a label with no icon nor tooltip. 1780 * @param l the label. 1781 */ 1782 public static void setNotAvailable(SelectableLabelWithHelpIcon l) 1783 { 1784 l.setText(INFO_CTRL_PANEL_NOT_AVAILABLE_LONG_LABEL.get().toString()); 1785 l.setHelpIconVisible(false); 1786 l.setHelpTooltip(null); 1787 } 1788 1789 /** 1790 * Sets the a text to a label with no icon nor tooltip. 1791 * @param l the label. 1792 * @param text the text. 1793 */ 1794 public static void setTextValue(SelectableLabelWithHelpIcon l, String text) 1795 { 1796 l.setText(text); 1797 l.setHelpIconVisible(false); 1798 l.setHelpTooltip(null); 1799 } 1800 1801 /** 1802 * Returns the server root directory (the path where the server is installed). 1803 * <p> 1804 * Note: this method is called by SNMP code. 1805 * 1806 * @return the server root directory (the path where the server is installed). 1807 */ 1808 public static File getServerRootDirectory() 1809 { 1810 if (rootDirectory == null) 1811 { 1812 // This allows testing of configuration components when the OpenDJ.jar 1813 // in the classpath does not necessarily point to the server's 1814 String installRoot = System.getProperty("org.opends.quicksetup.Root"); 1815 1816 if (installRoot == null) { 1817 installRoot = getInstallPathFromClasspath(); 1818 } 1819 rootDirectory = new File(installRoot); 1820 } 1821 return rootDirectory; 1822 } 1823 1824 /** 1825 * Returns the instance root directory (the path where the instance is 1826 * installed). 1827 * @param installPath The installRoot path. 1828 * @return the instance root directory (the path where the instance is 1829 * installed). 1830 */ 1831 public static File getInstanceRootDirectory(String installPath) 1832 { 1833 if (instanceRootDirectory == null) 1834 { 1835 instanceRootDirectory = new File( 1836 Utils.getInstancePathFromInstallPath(installPath)); 1837 } 1838 return instanceRootDirectory; 1839 } 1840 1841 /** 1842 * Returns the path of the installation of the directory server. Note that 1843 * this method assumes that this code is being run locally. 1844 * @return the path of the installation of the directory server. 1845 */ 1846 public static String getInstallPathFromClasspath() 1847 { 1848 String installPath = null; 1849 1850 /* Get the install path from the Class Path */ 1851 String sep = System.getProperty("path.separator"); 1852 String[] classPaths = System.getProperty("java.class.path").split(sep); 1853 String path = getInstallPath(classPaths); 1854 if (path != null) { 1855 File f = new File(path).getAbsoluteFile(); 1856 File librariesDir = f.getParentFile(); 1857 1858 /* 1859 * Do a best effort to avoid having a relative representation (for 1860 * instance to avoid having ../../../). 1861 */ 1862 try 1863 { 1864 installPath = librariesDir.getParentFile().getCanonicalPath(); 1865 } 1866 catch (IOException ioe) 1867 { 1868 // Best effort 1869 installPath = librariesDir.getParent(); 1870 } 1871 } 1872 return installPath; 1873 } 1874 1875 private static String getInstallPath(String[] classPaths) 1876 { 1877 for (String classPath : classPaths) 1878 { 1879 final String normPath = classPath.replace(File.separatorChar, '/'); 1880 if (normPath.endsWith(OPENDJ_BOOTSTRAP_CLIENT_JAR_RELATIVE_PATH) 1881 || normPath.endsWith(OPENDJ_BOOTSTRAP_JAR_RELATIVE_PATH)) 1882 { 1883 return classPath; 1884 } 1885 } 1886 return null; 1887 } 1888 1889 /** 1890 * Returns <CODE>true</CODE> if the server located in the provided path 1891 * is running and <CODE>false</CODE> otherwise. 1892 * @param serverRootDirectory the path where the server is installed. 1893 * @return <CODE>true</CODE> if the server located in the provided path 1894 * is running and <CODE>false</CODE> otherwise. 1895 */ 1896 public static boolean isServerRunning(File serverRootDirectory) 1897 { 1898 String lockFileName = ServerConstants.SERVER_LOCK_FILE_NAME + ServerConstants.LOCK_FILE_SUFFIX; 1899 String lockPathRelative = Installation.LOCKS_PATH_RELATIVE; 1900 File locksPath = new File(serverRootDirectory, lockPathRelative); 1901 String lockFile = new File(locksPath, lockFileName).getAbsolutePath(); 1902 StringBuilder failureReason = new StringBuilder(); 1903 try { 1904 if (LockFileManager.acquireExclusiveLock(lockFile, failureReason)) 1905 { 1906 LockFileManager.releaseLock(lockFile, failureReason); 1907 return false; 1908 } 1909 return true; 1910 } 1911 catch (Throwable t) { 1912 // Assume that if we cannot acquire the lock file the 1913 // server is running. 1914 return true; 1915 } 1916 } 1917 1918 private static final String VALID_SCHEMA_SYNTAX = 1919 "abcdefghijklmnopqrstuvwxyz0123456789-"; 1920 1921 /** 1922 * Returns <CODE>true</CODE> if the provided string can be used as objectclass 1923 * name and <CODE>false</CODE> otherwise. 1924 * @param s the string to be analyzed. 1925 * @return <CODE>true</CODE> if the provided string can be used as objectclass 1926 * name and <CODE>false</CODE> otherwise. 1927 */ 1928 private static boolean isValidObjectclassName(String s) 1929 { 1930 if (s == null || s.length() == 0) 1931 { 1932 return false; 1933 } 1934 1935 final StringCharacterIterator iter = new StringCharacterIterator(s, 0); 1936 char c = iter.first(); 1937 while (c != CharacterIterator.DONE) 1938 { 1939 if (VALID_SCHEMA_SYNTAX.indexOf(Character.toLowerCase(c)) == -1) 1940 { 1941 return false; 1942 } 1943 c = iter.next(); 1944 } 1945 return true; 1946 } 1947 1948 /** 1949 * Returns <CODE>true</CODE> if the provided string can be used as attribute 1950 * name and <CODE>false</CODE> otherwise. 1951 * @param s the string to be analyzed. 1952 * @return <CODE>true</CODE> if the provided string can be used as attribute 1953 * name and <CODE>false</CODE> otherwise. 1954 */ 1955 public static boolean isValidAttributeName(String s) 1956 { 1957 return isValidObjectclassName(s); 1958 } 1959 1960 /** 1961 * Returns the representation of the VLV index as it must be used in the 1962 * command-line. 1963 * @param index the VLV index. 1964 * @return the representation of the VLV index as it must be used in the 1965 * command-line. 1966 */ 1967 public static String getVLVNameInCommandLine(VLVIndexDescriptor index) 1968 { 1969 return "vlv."+index.getName(); 1970 } 1971 1972 /** 1973 * Returns a string representing the VLV index in a cell. 1974 * @param index the VLV index to be represented. 1975 * @return the string representing the VLV index in a cell. 1976 */ 1977 public static String getVLVNameInCellRenderer(VLVIndexDescriptor index) 1978 { 1979 return INFO_CTRL_PANEL_VLV_INDEX_CELL.get(index.getName()).toString(); 1980 } 1981 1982 private static final String[] standardSchemaFileNames = 1983 { 1984 "00-core.ldif", "01-pwpolicy.ldif", "03-changelog.ldif", 1985 "03-uddiv3.ldif", "05-solaris.ldif" 1986 }; 1987 1988 private static final String[] configurationSchemaOrigins = 1989 { 1990 "OpenDJ Directory Server", "OpenDS Directory Server", 1991 "Sun Directory Server", "Microsoft Active Directory" 1992 }; 1993 1994 private static final String[] standardSchemaOrigins = 1995 { 1996 "Sun Java System Directory Server", "Solaris Specific", "X.501" 1997 }; 1998 1999 private static final String[] configurationSchemaFileNames = 2000 { 2001 "02-config.ldif", "06-compat.ldif" 2002 }; 2003 2004 /** 2005 * Returns <CODE>true</CODE> if the provided schema element is part of the 2006 * standard and <CODE>false</CODE> otherwise. 2007 * @param fileElement the schema element. 2008 * @return <CODE>true</CODE> if the provided schema element is part of the 2009 * standard and <CODE>false</CODE> otherwise. 2010 */ 2011 public static boolean isStandard(SomeSchemaElement fileElement) 2012 { 2013 final String fileName = fileElement.getSchemaFile(); 2014 if (fileName != null) 2015 { 2016 return contains(standardSchemaFileNames, fileName) || fileName.toLowerCase().contains("-rfc"); 2017 } 2018 String xOrigin = getOrigin(fileElement); 2019 if (xOrigin != null) 2020 { 2021 return contains(standardSchemaOrigins, xOrigin) || xOrigin.startsWith("RFC ") || xOrigin.startsWith("draft-"); 2022 } 2023 return false; 2024 } 2025 2026 /** 2027 * Returns <CODE>true</CODE> if the provided schema element is part of the 2028 * configuration and <CODE>false</CODE> otherwise. 2029 * @param fileElement the schema element. 2030 * @return <CODE>true</CODE> if the provided schema element is part of the 2031 * configuration and <CODE>false</CODE> otherwise. 2032 */ 2033 public static boolean isConfiguration(SomeSchemaElement fileElement) 2034 { 2035 String fileName = fileElement.getSchemaFile(); 2036 if (fileName != null) 2037 { 2038 return contains(configurationSchemaFileNames, fileName); 2039 } 2040 String xOrigin = getOrigin(fileElement); 2041 if (xOrigin != null) 2042 { 2043 return contains(configurationSchemaOrigins, xOrigin); 2044 } 2045 return false; 2046 } 2047 2048 private static boolean contains(String[] names, String toFind) 2049 { 2050 for (String name : names) 2051 { 2052 if (toFind.equals(name)) 2053 { 2054 return true; 2055 } 2056 } 2057 return false; 2058 } 2059 2060 /** 2061 * Returns the origin of the provided schema element. 2062 * @param element the schema element. 2063 * @return the origin of the provided schema element. 2064 */ 2065 public static String getOrigin(SomeSchemaElement element) 2066 { 2067 return element.getOrigin(); 2068 } 2069 2070 /** 2071 * Returns the string representation of an attribute syntax. 2072 * @param syntax the attribute syntax. 2073 * @return the string representation of an attribute syntax. 2074 */ 2075 public static String getSyntaxText(Syntax syntax) 2076 { 2077 String syntaxName = syntax.getName(); 2078 String syntaxOID = syntax.getOID(); 2079 if (syntaxName != null) 2080 { 2081 return syntaxName + " - " + syntaxOID; 2082 } 2083 return syntaxOID; 2084 } 2085 2086 /** 2087 * Returns <CODE>true</CODE> if the provided attribute has image syntax and 2088 * <CODE>false</CODE> otherwise. 2089 * @param attrName the name of the attribute. 2090 * @param schema the schema. 2091 * @return <CODE>true</CODE> if the provided attribute has image syntax and 2092 * <CODE>false</CODE> otherwise. 2093 */ 2094 public static boolean hasImageSyntax(String attrName, Schema schema) 2095 { 2096 attrName = Utilities.getAttributeNameWithoutOptions(attrName); 2097 if ("photo".equals(attrName)) 2098 { 2099 return true; 2100 } 2101 // Check all the attributes that we consider binaries. 2102 if (schema != null && schema.hasAttributeType(attrName)) 2103 { 2104 AttributeType attr = schema.getAttributeType(attrName); 2105 String syntaxOID = attr.getSyntax().getOID(); 2106 return SchemaConstants.SYNTAX_JPEG_OID.equals(syntaxOID); 2107 } 2108 return false; 2109 } 2110 2111 /** 2112 * Returns <CODE>true</CODE> if the provided attribute has binary syntax and 2113 * <CODE>false</CODE> otherwise. 2114 * @param attrName the name of the attribute. 2115 * @param schema the schema. 2116 * @return <CODE>true</CODE> if the provided attribute has binary syntax and 2117 * <CODE>false</CODE> otherwise. 2118 */ 2119 public static boolean hasBinarySyntax(String attrName, Schema schema) 2120 { 2121 return attrName.toLowerCase().contains(";binary") 2122 || hasAnySyntax(attrName, schema, binarySyntaxOIDs); 2123 } 2124 2125 /** 2126 * Returns <CODE>true</CODE> if the provided attribute has password syntax and 2127 * <CODE>false</CODE> otherwise. 2128 * @param attrName the name of the attribute. 2129 * @param schema the schema. 2130 * @return <CODE>true</CODE> if the provided attribute has password syntax and 2131 * <CODE>false</CODE> otherwise. 2132 */ 2133 public static boolean hasPasswordSyntax(String attrName, Schema schema) 2134 { 2135 return hasAnySyntax(attrName, schema, passwordSyntaxOIDs); 2136 } 2137 2138 private static boolean hasAnySyntax(String attrName, Schema schema, String[] oids) 2139 { 2140 if (schema != null) 2141 { 2142 attrName = Utilities.getAttributeNameWithoutOptions(attrName).toLowerCase(); 2143 if (schema.hasAttributeType(attrName)) 2144 { 2145 AttributeType attr = schema.getAttributeType(attrName); 2146 return contains(oids, attr.getSyntax().getOID()); 2147 } 2148 } 2149 return false; 2150 } 2151 2152 /** 2153 * Returns the string representation of a matching rule. 2154 * @param matchingRule the matching rule. 2155 * @return the string representation of a matching rule. 2156 */ 2157 public static String getMatchingRuleText(MatchingRule matchingRule) 2158 { 2159 String nameOrOID = matchingRule.getNameOrOID(); 2160 String oid = matchingRule.getOID(); 2161 if (!nameOrOID.equals(oid)) 2162 { 2163 // This is the name only 2164 return nameOrOID + " - " + oid; 2165 } 2166 return oid; 2167 } 2168 2169 /** 2170 * Returns the InitialLdapContext to connect to the administration connector 2171 * of the server using the information in the ControlCenterInfo object (which 2172 * provides the host and administration connector port to be used) and some 2173 * LDAP credentials. 2174 * It also tests that the provided credentials have enough rights to read the 2175 * configuration. 2176 * @param controlInfo the object which provides the connection parameters. 2177 * @param bindDN the base DN to be used to bind. 2178 * @param pwd the password to be used to bind. 2179 * @return the InitialLdapContext connected to the server. 2180 * @throws NamingException if there was a problem connecting to the server 2181 * or the provided credentials do not have enough rights. 2182 * @throws ConfigReadException if there is an error reading the configuration. 2183 */ 2184 public static InitialLdapContext getAdminDirContext( 2185 ControlPanelInfo controlInfo, String bindDN, String pwd) 2186 throws NamingException, ConfigReadException 2187 { 2188 String usedUrl = controlInfo.getAdminConnectorURL(); 2189 if (usedUrl == null) 2190 { 2191 throw new ConfigReadException( 2192 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2193 } 2194 2195 InitialLdapContext ctx = createLdapsContext(usedUrl, 2196 bindDN, pwd, controlInfo.getConnectTimeout(), null, 2197 controlInfo.getTrustManager(), null); 2198 // Search for the config to check that it is the directory manager. 2199 checkCanReadConfig(ctx); 2200 return ctx; 2201 } 2202 2203 2204 /** 2205 * Returns the InitialLdapContext to connect to the server using the 2206 * information in the ControlCenterInfo object (which provides the host, port 2207 * and protocol to be used) and some LDAP credentials. It also tests that 2208 * the provided credentials have enough rights to read the configuration. 2209 * @param controlInfo the object which provides the connection parameters. 2210 * @param bindDN the base DN to be used to bind. 2211 * @param pwd the password to be used to bind. 2212 * @return the InitialLdapContext connected to the server. 2213 * @throws NamingException if there was a problem connecting to the server 2214 * or the provided credentials do not have enough rights. 2215 * @throws ConfigReadException if there is an error reading the configuration. 2216 */ 2217 public static InitialLdapContext getUserDataDirContext( 2218 ControlPanelInfo controlInfo, 2219 String bindDN, String pwd) throws NamingException, ConfigReadException 2220 { 2221 InitialLdapContext ctx; 2222 String usedUrl; 2223 if (controlInfo.connectUsingStartTLS()) 2224 { 2225 usedUrl = controlInfo.getStartTLSURL(); 2226 if (usedUrl == null) 2227 { 2228 throw new ConfigReadException( 2229 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2230 } 2231 ctx = Utils.createStartTLSContext(usedUrl, 2232 bindDN, pwd, controlInfo.getConnectTimeout(), null, 2233 controlInfo.getTrustManager(), null); 2234 } 2235 else if (controlInfo.connectUsingLDAPS()) 2236 { 2237 usedUrl = controlInfo.getLDAPSURL(); 2238 if (usedUrl == null) 2239 { 2240 throw new ConfigReadException( 2241 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2242 } 2243 ctx = createLdapsContext(usedUrl, 2244 bindDN, pwd, controlInfo.getConnectTimeout(), null, 2245 controlInfo.getTrustManager(), null); 2246 } 2247 else 2248 { 2249 usedUrl = controlInfo.getLDAPURL(); 2250 if (usedUrl == null) 2251 { 2252 throw new ConfigReadException( 2253 ERR_COULD_NOT_FIND_VALID_LDAPURL.get()); 2254 } 2255 ctx = createLdapContext(usedUrl, 2256 bindDN, pwd, controlInfo.getConnectTimeout(), null); 2257 } 2258 2259 checkCanReadConfig(ctx); 2260 return ctx; 2261 } 2262 2263 /** 2264 * Checks that the provided connection can read cn=config. 2265 * @param ctx the connection to be tested. 2266 * @throws NamingException if an error occurs while reading cn=config. 2267 */ 2268 private static void checkCanReadConfig(InitialLdapContext ctx) 2269 throws NamingException 2270 { 2271 // Search for the config to check that it is the directory manager. 2272 SearchControls searchControls = new SearchControls(); 2273 searchControls.setSearchScope(SearchControls.OBJECT_SCOPE); 2274 searchControls.setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES }); 2275 NamingEnumeration<SearchResult> sr = 2276 ctx.search("cn=config", "objectclass=*", searchControls); 2277 try 2278 { 2279 while (sr.hasMore()) 2280 { 2281 sr.next(); 2282 } 2283 } 2284 finally 2285 { 2286 sr.close(); 2287 } 2288 } 2289 2290 /** 2291 * Ping the specified InitialLdapContext. 2292 * This method sends a search request on the root entry of the DIT 2293 * and forward the corresponding exception (if any). 2294 * @param ctx the InitialLdapContext to be "pinged". 2295 * @throws NamingException if the ping could not be performed. 2296 */ 2297 public static void pingDirContext(InitialLdapContext ctx) 2298 throws NamingException { 2299 SearchControls sc = new SearchControls( 2300 SearchControls.OBJECT_SCOPE, 2301 0, // count limit 2302 0, // time limit 2303 new String[0], // No attributes 2304 false, // Don't return bound object 2305 false // Don't dereference link 2306 ); 2307 ctx.search("", "objectClass=*", sc); 2308 } 2309 2310 /** 2311 * Deletes a configuration subtree using the provided configuration handler. 2312 * @param confHandler the configuration handler to be used to delete the 2313 * subtree. 2314 * @param dn the DN of the subtree to be deleted. 2315 * @throws OpenDsException if an error occurs. 2316 * @throws ConfigException if an error occurs. 2317 */ 2318 public static void deleteConfigSubtree(ConfigHandler confHandler, DN dn) 2319 throws OpenDsException, ConfigException 2320 { 2321 ConfigEntry confEntry = confHandler.getConfigEntry(dn); 2322 if (confEntry != null) 2323 { 2324 // Copy the values to avoid problems with this recursive method. 2325 ArrayList<DN> childDNs = new ArrayList<>(confEntry.getChildren().keySet()); 2326 for (DN childDN : childDNs) 2327 { 2328 deleteConfigSubtree(confHandler, childDN); 2329 } 2330 confHandler.deleteEntry(dn, null); 2331 } 2332 } 2333 2334 2335 /** 2336 * Sets the required icon to the provided label. 2337 * @param label the label to be updated. 2338 */ 2339 public static void setRequiredIcon(JLabel label) 2340 { 2341 if (requiredIcon == null) 2342 { 2343 requiredIcon = 2344 createImageIcon(IconPool.IMAGE_PATH+"/required.gif"); 2345 requiredIcon.setDescription( 2346 INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 2347 requiredIcon.getAccessibleContext().setAccessibleName( 2348 INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION.get().toString()); 2349 } 2350 label.setIcon(requiredIcon); 2351 label.setHorizontalTextPosition(SwingConstants.LEADING); 2352 } 2353 2354 2355 /** 2356 * Updates the scrolls with the provided points. 2357 * This method uses SwingUtilities.invokeLater so it can be also called 2358 * outside the event thread. 2359 * @param pos the scroll and points. 2360 */ 2361 public static void updateViewPositions(final ViewPositions pos) 2362 { 2363 SwingUtilities.invokeLater(new Runnable() 2364 { 2365 @Override 2366 public void run() 2367 { 2368 for (int i=0; i<pos.size(); i++) 2369 { 2370 pos.getScrollPane(i).getViewport().setViewPosition(pos.getPoint(i)); 2371 } 2372 } 2373 }); 2374 } 2375 2376 /** 2377 * Gets the view positions object for the provided component. This includes 2378 * all the scroll panes inside the provided component. 2379 * @param comp the component. 2380 * @return the view positions for the provided component. 2381 */ 2382 public static ViewPositions getViewPositions(Component comp) 2383 { 2384 ViewPositions pos = new ViewPositions(); 2385 if (comp instanceof Container) 2386 { 2387 updateContainedViewPositions((Container)comp, pos); 2388 } 2389 return pos; 2390 } 2391 2392 /** 2393 * Returns the scrolpane where the provided component is contained. 2394 * <CODE>null</CODE> if the component is not contained in any scrolpane. 2395 * @param comp the component. 2396 * @return the scrolpane where the provided component is contained. 2397 */ 2398 public static JScrollPane getContainingScroll(Component comp) 2399 { 2400 JScrollPane scroll = null; 2401 Container parent = comp.getParent(); 2402 while (scroll == null && parent != null) 2403 { 2404 if (parent instanceof JScrollPane) 2405 { 2406 scroll = (JScrollPane)parent; 2407 } 2408 else 2409 { 2410 parent = parent.getParent(); 2411 } 2412 } 2413 return scroll; 2414 } 2415 2416 private static void updateContainedViewPositions(Container comp, 2417 ViewPositions pos) 2418 { 2419 if (comp instanceof JScrollPane) 2420 { 2421 JScrollPane scroll = (JScrollPane)comp; 2422 Point p = scroll.getViewport().getViewPosition(); 2423 pos.add(scroll, p); 2424 } 2425 else 2426 { 2427 for (int i=0; i<comp.getComponentCount(); i++) 2428 { 2429 Component child = comp.getComponent(i); 2430 if (child instanceof Container) 2431 { 2432 updateContainedViewPositions((Container)child, pos); 2433 } 2434 } 2435 } 2436 } 2437 2438 private static Object getFirstMonitoringValue(CustomSearchResult sr, String attrName) 2439 { 2440 if (sr != null) 2441 { 2442 List<Object> values = sr.getAttributeValues(attrName); 2443 if (values != null && !values.isEmpty()) 2444 { 2445 Object o = values.iterator().next(); 2446 try 2447 { 2448 return Long.parseLong(o.toString()); 2449 } 2450 catch (Throwable t1) 2451 { 2452 try 2453 { 2454 return Double.parseDouble(o.toString()); 2455 } 2456 catch (Throwable t2) 2457 { 2458 // Cannot convert it, just return it 2459 return o; 2460 } 2461 } 2462 } 2463 } 2464 return null; 2465 } 2466 2467 /** 2468 * Returns the first value as a String for a given attribute in the provided 2469 * entry. 2470 * 2471 * @param sr 2472 * the entry. It may be <CODE>null</CODE>. 2473 * @param attrName 2474 * the attribute name. 2475 * @return the first value as a String for a given attribute in the provided 2476 * entry. 2477 */ 2478 public static String getFirstValueAsString(CustomSearchResult sr, String attrName) 2479 { 2480 if (sr != null) 2481 { 2482 final List<Object> values = sr.getAttributeValues(attrName); 2483 if (values != null && !values.isEmpty()) 2484 { 2485 final Object o = values.get(0); 2486 if (o != null) 2487 { 2488 return String.valueOf(o); 2489 } 2490 } 2491 } 2492 return null; 2493 } 2494 2495 /** 2496 * Returns the monitoring value in a String form to be displayed to the user. 2497 * @param attr the attribute to analyze. 2498 * @param monitoringEntry the monitoring entry. 2499 * @return the monitoring value in a String form to be displayed to the user. 2500 */ 2501 public static String getMonitoringValue(MonitoringAttributes attr, 2502 CustomSearchResult monitoringEntry) 2503 { 2504 String monitoringValue = getFirstValueAsString(monitoringEntry, attr.getAttributeName()); 2505 if (monitoringValue == null) 2506 { 2507 return NO_VALUE_SET.toString(); 2508 } 2509 else if (isNotImplemented(attr, monitoringEntry)) 2510 { 2511 return NOT_IMPLEMENTED.toString(); 2512 } 2513 else if (attr.isNumericDate()) 2514 { 2515 if ("0".equals(monitoringValue)) 2516 { 2517 return NO_VALUE_SET.toString(); 2518 } 2519 Long l = Long.parseLong(monitoringValue); 2520 Date date = new Date(l); 2521 return ConfigFromDirContext.formatter.format(date); 2522 } 2523 else if (attr.isTime()) 2524 { 2525 if ("-1".equals(monitoringValue)) 2526 { 2527 return NO_VALUE_SET.toString(); 2528 } 2529 return monitoringValue; 2530 } 2531 else if (attr.isGMTDate()) 2532 { 2533 try 2534 { 2535 Date date = ConfigFromDirContext.utcParser.parse(monitoringValue); 2536 return ConfigFromDirContext.formatter.format(date); 2537 } 2538 catch (Throwable t) 2539 { 2540 return monitoringValue; 2541 } 2542 } 2543 else if (attr.isValueInBytes()) 2544 { 2545 Long l = Long.parseLong(monitoringValue); 2546 long mb = l / (1024 * 1024); 2547 long kbs = (l - mb * 1024 * 1024) / 1024; 2548 return INFO_CTRL_PANEL_MEMORY_VALUE.get(mb, kbs).toString(); 2549 } 2550 return monitoringValue; 2551 } 2552 2553 /** 2554 * Returns <CODE>true</CODE> if the provided monitoring value represents the 2555 * non implemented label and <CODE>false</CODE> otherwise. 2556 * @param attr the attribute to analyze. 2557 * @param monitoringEntry the monitoring entry. 2558 * @return <CODE>true</CODE> if the provided monitoring value represents the 2559 * non implemented label and <CODE>false</CODE> otherwise. 2560 */ 2561 private static boolean isNotImplemented(MonitoringAttributes attr, 2562 CustomSearchResult monitoringEntry) 2563 { 2564 String monitoringValue = getFirstValueAsString(monitoringEntry, attr.getAttributeName()); 2565 if (attr.isNumeric() && monitoringValue != null) 2566 { 2567 try 2568 { 2569 Long.parseLong(monitoringValue); 2570 return false; 2571 } 2572 catch (Throwable t) 2573 { 2574 return true; 2575 } 2576 } 2577 return false; 2578 } 2579 2580 /** 2581 * Adds a click tool tip listener to the provided component. 2582 * @param comp the component. 2583 */ 2584 public static void addClickTooltipListener(JComponent comp) 2585 { 2586 comp.addMouseListener(new ClickTooltipDisplayer()); 2587 } 2588 2589 /** 2590 * Updates a combo box model with a number of items. 2591 * The method assumes that is being called from the event thread. 2592 * @param newElements the new items for the combo box model. 2593 * @param model the combo box model to be updated. 2594 */ 2595 public static void updateComboBoxModel(Collection<?> newElements, 2596 DefaultComboBoxModel model) 2597 { 2598 updateComboBoxModel(newElements, model, null); 2599 } 2600 2601 /** 2602 * Updates a combo box model with a number of items. 2603 * The method assumes that is being called from the event thread. 2604 * @param newElements the new items for the combo box model. 2605 * @param model the combo box model to be updated. 2606 * @param comparator the object that will be used to compare the objects in 2607 * the model. If <CODE>null</CODE>, the equals method will be used. 2608 */ 2609 public static void updateComboBoxModel(Collection<?> newElements, 2610 DefaultComboBoxModel model, 2611 Comparator<Object> comparator) 2612 { 2613 boolean changed = newElements.size() != model.getSize(); 2614 if (!changed) 2615 { 2616 int i = 0; 2617 for (Object newElement : newElements) 2618 { 2619 if (comparator != null) 2620 { 2621 changed = 2622 comparator.compare(newElement, model.getElementAt(i)) != 0; 2623 } 2624 else 2625 { 2626 changed = !newElement.equals(model.getElementAt(i)); 2627 } 2628 if (changed) 2629 { 2630 break; 2631 } 2632 i++; 2633 } 2634 } 2635 if (changed) 2636 { 2637 Object selected = model.getSelectedItem(); 2638 model.removeAllElements(); 2639 boolean selectDefault = false; 2640 for (Object newElement : newElements) 2641 { 2642 model.addElement(newElement); 2643 } 2644 if (selected != null) 2645 { 2646 if (model.getIndexOf(selected) != -1) 2647 { 2648 model.setSelectedItem(selected); 2649 } 2650 else 2651 { 2652 selectDefault = true; 2653 } 2654 } 2655 else 2656 { 2657 selectDefault = true; 2658 } 2659 if (selectDefault) 2660 { 2661 for (int i=0; i<model.getSize(); i++) 2662 { 2663 Object o = model.getElementAt(i); 2664 if (o instanceof CategorizedComboBoxElement 2665 && ((CategorizedComboBoxElement)o).getType() == CategorizedComboBoxElement.Type.CATEGORY) 2666 { 2667 continue; 2668 } 2669 model.setSelectedItem(o); 2670 break; 2671 } 2672 } 2673 } 2674 } 2675 2676 /** 2677 * Computes the possible comparison results for monitoring information. 2678 * 2679 * @param monitor1 2680 * the first monitor to compare 2681 * @param monitor2 2682 * the second monitor to compare 2683 * @param possibleResults 2684 * where possible results are output 2685 * @param attrNames 2686 * the names for which to compute possible comparison results 2687 */ 2688 public static void computeMonitoringPossibleResults(CustomSearchResult monitor1, CustomSearchResult monitor2, 2689 ArrayList<Integer> possibleResults, Collection<String> attrNames) 2690 { 2691 for (String attrName : attrNames) 2692 { 2693 int possibleResult; 2694 if (monitor1 == null) 2695 { 2696 if (monitor2 == null) 2697 { 2698 possibleResult = 0; 2699 } 2700 else 2701 { 2702 possibleResult = -1; 2703 } 2704 } 2705 else if (monitor2 == null) 2706 { 2707 possibleResult = 1; 2708 } 2709 else 2710 { 2711 Object v1 = getFirstValue(monitor1, attrName); 2712 Object v2 = getFirstValue(monitor2, attrName); 2713 if (v1 == null) 2714 { 2715 if (v2 == null) 2716 { 2717 possibleResult = 0; 2718 } 2719 else 2720 { 2721 possibleResult = -1; 2722 } 2723 } 2724 else if (v2 == null) 2725 { 2726 possibleResult = 1; 2727 } 2728 else if (v1 instanceof Number) 2729 { 2730 if (v2 instanceof Number) 2731 { 2732 if (v1 instanceof Double || v2 instanceof Double) 2733 { 2734 double n1 = ((Number) v1).doubleValue(); 2735 double n2 = ((Number) v2).doubleValue(); 2736 if (n1 > n2) 2737 { 2738 possibleResult = 1; 2739 } 2740 else if (n1 < n2) 2741 { 2742 possibleResult = -1; 2743 } 2744 else 2745 { 2746 possibleResult = 0; 2747 } 2748 } 2749 else 2750 { 2751 long n1 = ((Number) v1).longValue(); 2752 long n2 = ((Number) v2).longValue(); 2753 if (n1 > n2) 2754 { 2755 possibleResult = 1; 2756 } 2757 else if (n1 < n2) 2758 { 2759 possibleResult = -1; 2760 } 2761 else 2762 { 2763 possibleResult = 0; 2764 } 2765 } 2766 } 2767 else 2768 { 2769 possibleResult = 1; 2770 } 2771 } 2772 else if (v2 instanceof Number) 2773 { 2774 possibleResult = -1; 2775 } 2776 else 2777 { 2778 possibleResult = v1.toString().compareTo(v2.toString()); 2779 } 2780 } 2781 possibleResults.add(possibleResult); 2782 } 2783 } 2784 2785 private static Object getFirstValue(CustomSearchResult monitor, String attrName) 2786 { 2787 for (String attr : monitor.getAttributeNames()) 2788 { 2789 if (attr.equalsIgnoreCase(attrName)) 2790 { 2791 return getFirstMonitoringValue(monitor, attrName); 2792 } 2793 } 2794 return null; 2795 } 2796 2797 /** 2798 * Throw the first exception of the list (if any). 2799 * 2800 * @param <E> 2801 * The exception type 2802 * @param exceptions 2803 * A list of exceptions. 2804 * @throws E 2805 * The first element of the provided list (if the list is not 2806 * empty). 2807 */ 2808 public static <E extends Exception> void throwFirstFrom(List<? extends E> exceptions) throws E 2809 { 2810 if (!exceptions.isEmpty()) 2811 { 2812 throw exceptions.get(0); 2813 } 2814 } 2815 2816 /** 2817 * Initialize the configuration framework. 2818 */ 2819 public static void initializeConfigurationFramework() 2820 { 2821 if (!ConfigurationFramework.getInstance().isInitialized()) 2822 { 2823 try 2824 { 2825 ConfigurationFramework.getInstance().initialize(); 2826 } 2827 catch (ConfigException e) 2828 { 2829 final LocalizableMessage message = ERROR_CTRL_PANEL_INITIALIZE_CONFIG_OFFLINE.get(e.getLocalizedMessage()); 2830 logger.error(message); 2831 throw new RuntimeException(message.toString(), e); 2832 } 2833 } 2834 } 2835 2836 /** Initialize the legacy configuration framework. */ 2837 public static void initializeLegacyConfigurationFramework() 2838 { 2839 try 2840 { 2841 final ClassLoaderProvider provider = ClassLoaderProvider.getInstance(); 2842 if (!provider.isEnabled()) 2843 { 2844 provider.enable(); 2845 } 2846 } 2847 catch (Exception e) 2848 { 2849 final LocalizableMessage message = ERROR_CTRL_PANEL_INITIALIZE_CONFIG_OFFLINE.get(e.getLocalizedMessage()); 2850 logger.error(message); 2851 throw new RuntimeException(message.toString(), e); 2852 } 2853 2854 } 2855 2856}