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 2012-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.awt.Component;
032import java.awt.GridBagConstraints;
033import java.awt.Point;
034import java.io.IOException;
035import java.io.StringReader;
036import java.util.List;
037
038import javax.swing.JLabel;
039import javax.swing.JScrollPane;
040import javax.swing.JTextArea;
041import javax.swing.SwingUtilities;
042import javax.swing.event.DocumentEvent;
043import javax.swing.event.DocumentListener;
044import javax.swing.tree.TreePath;
045
046import org.forgerock.i18n.LocalizableMessage;
047import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
048import org.opends.guitools.controlpanel.task.OfflineUpdateException;
049import org.opends.guitools.controlpanel.util.Utilities;
050import org.opends.server.types.Entry;
051import org.opends.server.types.LDIFImportConfig;
052import org.opends.server.types.OpenDsException;
053import org.opends.server.util.Base64;
054import org.opends.server.util.LDIFReader;
055import org.opends.server.util.StaticUtils;
056
057/**
058 * The panel displaying an LDIF view of an entry.
059 */
060public class LDIFViewEntryPanel extends ViewEntryPanel
061{
062  /** Callback that sets the viewport's view position. */
063  private static final class SetViewPosition implements Runnable
064  {
065    private final Point p;
066    private final JScrollPane scroll;
067
068    private SetViewPosition(JScrollPane scroll, Point p)
069    {
070      this.p = p;
071      this.scroll = scroll;
072    }
073
074    /** {@inheritDoc} */
075    @Override
076    public void run()
077    {
078      if (p != null && scroll.getViewport().contains(p))
079      {
080        scroll.getViewport().setViewPosition(p);
081      }
082    }
083  }
084
085  private static final long serialVersionUID = 2775960608128921072L;
086  private JScrollPane editableScroll;
087  private JScrollPane readOnlyScroll;
088  private JTextArea editableAttributes;
089  private JTextArea readOnlyAttributes;
090  private CustomSearchResult searchResult;
091
092  /** Default constructor. */
093  public LDIFViewEntryPanel()
094  {
095    createLayout();
096  }
097
098  /** {@inheritDoc} */
099  @Override
100  public Component getPreferredFocusComponent()
101  {
102    return editableAttributes;
103  }
104
105  /**
106   * Creates the layout of the panel (but the contents are not populated here).
107   */
108  private void createLayout()
109  {
110    GridBagConstraints gbc = new GridBagConstraints();
111    gbc.gridx = 0;
112    gbc.gridy = 0;
113    gbc.gridwidth = 1;
114    gbc.fill = GridBagConstraints.NONE;
115    gbc.anchor = GridBagConstraints.WEST;
116    gbc.weightx = 1.0;
117
118    addTitlePanel(this, gbc);
119
120    gbc.gridy ++;
121    gbc.insets.top = 10;
122
123    editableAttributes = Utilities.createTextArea(LocalizableMessage.EMPTY, 20, 30);
124    editableAttributes.getDocument().addDocumentListener(new DocumentListener()
125    {
126      @Override
127      public void insertUpdate(DocumentEvent ev)
128      {
129        notifyListeners();
130      }
131
132      @Override
133      public void changedUpdate(DocumentEvent ev)
134      {
135        notifyListeners();
136      }
137
138      @Override
139      public void removeUpdate(DocumentEvent ev)
140      {
141        notifyListeners();
142      }
143    });
144    gbc.weighty = 0.6;
145    gbc.fill = GridBagConstraints.BOTH;
146    gbc.gridy ++;
147    editableScroll = Utilities.createScrollPane(editableAttributes);
148    add(editableScroll, gbc);
149
150
151    gbc.weighty = 0.0;
152    gbc.insets.top = 10;
153    JLabel lReadOnly = Utilities.createPrimaryLabel(
154        INFO_CTRL_PANEL_NON_EDITABLE_ATTRIBUTES.get());
155    gbc.gridy ++;
156    add(lReadOnly, gbc);
157    gbc.insets.top = 5;
158    readOnlyAttributes = Utilities.createNonEditableTextArea(LocalizableMessage.EMPTY, 10, 30);
159    gbc.weightx = 1.0;
160    gbc.weighty = 0.4;
161    gbc.fill = GridBagConstraints.BOTH;
162    gbc.gridy ++;
163    readOnlyScroll = Utilities.createScrollPane(readOnlyAttributes);
164    add(readOnlyScroll, gbc);
165  }
166
167  /** {@inheritDoc} */
168  @Override
169  public void update(CustomSearchResult sr, boolean isReadOnly, TreePath path)
170  {
171    boolean sameEntry = false;
172    if (searchResult != null && sr != null)
173    {
174      sameEntry = searchResult.getDN().equals(sr.getDN());
175    }
176
177    searchResult = sr;
178    updateTitle(sr, path);
179
180    StringBuilder sb = new StringBuilder();
181    sb.append("dn: ").append(sr.getDN());
182
183    if (isReadOnly)
184    {
185      editableScroll.setVisible(false);
186      for (String attrName : sr.getAttributeNames())
187      {
188        List<Object> values = sr.getAttributeValues(attrName);
189        for (Object o : values)
190        {
191          sb.append("\n").append(getLDIFLine(attrName, o));
192        }
193      }
194      final Point p1 = sameEntry ?
195          readOnlyScroll.getViewport().getViewPosition() : new Point(0, 0);
196      readOnlyAttributes.setText(sb.toString());
197      SwingUtilities.invokeLater(new SetViewPosition(readOnlyScroll, p1));
198    }
199    else
200    {
201      editableScroll.setVisible(true);
202
203      for (String attrName : sr.getAttributeNames())
204      {
205        if (!schemaReadOnlyAttributesLowerCase.contains(attrName.toLowerCase()))
206        {
207          List<Object> values = sr.getAttributeValues(attrName);
208          for (Object o : values)
209          {
210            sb.append("\n").append(getLDIFLine(attrName, o));
211          }
212        }
213      }
214      final Point p1 = sameEntry ?
215          editableScroll.getViewport().getViewPosition() : new Point(0, 0);
216      ignoreEntryChangeEvents = true;
217      editableAttributes.setText(sb.toString());
218      ignoreEntryChangeEvents = false;
219
220      SwingUtilities.invokeLater(new SetViewPosition(editableScroll, p1));
221      // Read-only attributes
222      boolean oneLineAdded = false;
223      sb = new StringBuilder();
224      for (String attrName : schemaReadOnlyAttributes)
225      {
226        List<Object> values = sr.getAttributeValues(attrName);
227        for (Object o : values)
228        {
229          if (oneLineAdded)
230          {
231            sb.append("\n");
232          }
233          oneLineAdded = true;
234          sb.append(getLDIFLine(attrName, o));
235        }
236      }
237      final Point p2 = sameEntry ?
238          readOnlyScroll.getViewport().getViewPosition() : new Point(0, 0);
239      readOnlyAttributes.setText(sb.toString());
240      SwingUtilities.invokeLater(new SetViewPosition(readOnlyScroll, p2));
241    }
242  }
243
244  /** {@inheritDoc} */
245  @Override
246  public GenericDialog.ButtonType getButtonType()
247  {
248    return GenericDialog.ButtonType.NO_BUTTON;
249  }
250
251
252  /** {@inheritDoc} */
253  @Override
254  protected String getDisplayedDN()
255  {
256    String dn = null;
257    // Do it fast, this is called to update the dn displayed in the title.
258    String ldif = getLDIF();
259    int index = ldif.toLowerCase().indexOf("dn: ");
260    if (index != -1)
261    {
262      int index2 = ldif.indexOf("\n", index);
263      if (index2 != -1)
264      {
265        dn = ldif.substring(index + 3, index2).trim();
266      }
267    }
268    return dn;
269  }
270
271  /** {@inheritDoc} */
272  @Override
273  protected List<Object> getValues(String attrName)
274  {
275    throw new IllegalStateException("This method should not be called.");
276  }
277
278  /** {@inheritDoc} */
279  @Override
280  public Entry getEntry() throws OpenDsException
281  {
282    LDIFImportConfig ldifImportConfig = null;
283    try
284    {
285      String ldif = getLDIF();
286
287      ldifImportConfig = new LDIFImportConfig(new StringReader(ldif));
288      LDIFReader reader = new LDIFReader(ldifImportConfig);
289      Entry entry = reader.readEntry(checkSchema());
290      addValuesInRDN(entry);
291      return entry;
292    }
293    catch (IOException ioe)
294    {
295      throw new OfflineUpdateException(
296          ERR_CTRL_PANEL_ERROR_CHECKING_ENTRY.get(ioe), ioe);
297    }
298    finally
299    {
300      if (ldifImportConfig != null)
301      {
302        ldifImportConfig.close();
303      }
304    }
305  }
306
307  /**
308   * Returns the LDIF representation of the entry, only returns the editable
309   * attributes.
310   * @return the LDIF representation of the entry.
311   */
312  private String getLDIF()
313  {
314    return editableAttributes.getText();
315  }
316
317  /**
318   * Returns the equivalent LDIF line for a given attribute and value.
319   * @param attrName the attribute name.
320   * @param o the value.
321   * @return the equivalent LDIF line for the provided attribute and value.
322   */
323  private String getLDIFLine(String attrName, Object o)
324  {
325    String attrValue;
326    if (o instanceof String)
327    {
328      if (Utilities.hasControlCharaters((String)o))
329      {
330        attrValue = Base64.encode(StaticUtils.getBytes((String)o));
331        attrName = attrName+":";
332      }
333      else
334      {
335        attrValue = (String)o;
336      }
337    }
338    else if (o instanceof byte[])
339    {
340      attrValue = Base64.encode((byte[])o);
341      // To indicate that is base64 encoded
342      attrName = attrName+":";
343    }
344    else
345    {
346      attrValue = String.valueOf(o);
347    }
348
349    return attrName+": "+ attrValue;
350  }
351}