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}