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