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}