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