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