/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
|
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at
|
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
|
* add the following below this CDDL HEADER, with the fields enclosed
|
* by brackets "[]" replaced with your own identifying information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2008 Sun Microsystems, Inc.
|
*/
|
|
package org.opends.guitools.controlpanel.ui;
|
|
import static org.opends.messages.AdminToolMessages.*;
|
|
import java.awt.Component;
|
import java.awt.GridBagConstraints;
|
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionListener;
|
import java.io.ByteArrayOutputStream;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.util.ArrayList;
|
import java.util.logging.Level;
|
import java.util.logging.Logger;
|
|
import javax.swing.Box;
|
import javax.swing.ButtonGroup;
|
import javax.swing.Icon;
|
import javax.swing.JButton;
|
import javax.swing.JLabel;
|
import javax.swing.JRadioButton;
|
import javax.swing.JTextField;
|
import javax.swing.text.JTextComponent;
|
|
import org.opends.guitools.controlpanel.datamodel.BinaryValue;
|
import org.opends.guitools.controlpanel.event.BrowseActionListener;
|
import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
|
import org.opends.guitools.controlpanel.util.BackgroundTask;
|
import org.opends.guitools.controlpanel.util.Utilities;
|
import org.opends.messages.Message;
|
import org.opends.server.types.Schema;
|
|
/**
|
* Panel that is displayed in the dialog where the user can specify the value
|
* of a binary attribute.
|
*
|
*/
|
public class BinaryAttributeEditorPanel extends StatusGenericPanel
|
{
|
private static final long serialVersionUID = -877248486446244170L;
|
private JRadioButton useFile;
|
private JRadioButton useBase64;
|
private JTextField file;
|
private JButton browse;
|
private JLabel lFile;
|
private JTextField base64;
|
private JLabel imagePreview;
|
private JButton refreshButton;
|
private JLabel lImage = Utilities.createDefaultLabel();
|
private JLabel attrName;
|
|
private BinaryValue value;
|
|
private boolean valueChanged;
|
|
private final static int MAX_IMAGE_HEIGHT = 300;
|
private final static int MAX_BASE64_TO_DISPLAY = 3 * 1024;
|
|
private static final Logger LOG =
|
Logger.getLogger(BinaryAttributeEditorPanel.class.getName());
|
|
/**
|
* Default constructor.
|
*
|
*/
|
public BinaryAttributeEditorPanel()
|
{
|
super();
|
createLayout();
|
}
|
|
/**
|
* Sets the value to be displayed in the panel.
|
* @param attrName the attribute name.
|
* @param value the binary value.
|
*/
|
public void setValue(final String attrName,
|
final BinaryValue value)
|
{
|
final boolean launchBackground = this.value != value;
|
// Read the file or encode the base 64 content.
|
BackgroundTask<Void> worker = new BackgroundTask<Void>()
|
{
|
/**
|
* {@inheritDoc}
|
*/
|
public Void processBackgroundTask() throws Throwable
|
{
|
try
|
{
|
Thread.sleep(1000);
|
}
|
catch (Throwable t)
|
{
|
}
|
valueChanged = false;
|
BinaryAttributeEditorPanel.this.attrName.setText(attrName);
|
if (hasImageSyntax(attrName))
|
{
|
if (value != null)
|
{
|
BinaryAttributeEditorPanel.updateImage(lImage, value.getBytes());
|
}
|
else
|
{
|
lImage.setIcon(null);
|
lImage.setText(
|
INFO_CTRL_PANEL_NO_VALUE_SPECIFIED.get().toString());
|
}
|
setImageVisible(true);
|
useFile.setSelected(true);
|
base64.setText("");
|
}
|
else
|
{
|
lImage.setIcon(null);
|
lImage.setText("");
|
setImageVisible(false);
|
|
if (value != null)
|
{
|
BinaryAttributeEditorPanel.updateBase64(base64, value.getBytes());
|
}
|
}
|
|
if (value != null)
|
{
|
if (value.getType() == BinaryValue.Type.BASE64_STRING)
|
{
|
file.setText("");
|
}
|
else
|
{
|
file.setText(value.getFile().getAbsolutePath());
|
useFile.setSelected(true);
|
}
|
}
|
else
|
{
|
base64.setText("");
|
file.setText("");
|
useFile.setSelected(true);
|
}
|
|
BinaryAttributeEditorPanel.this.value = value;
|
|
return null;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void backgroundTaskCompleted(Void returnValue,
|
Throwable t)
|
{
|
setPrimaryValid(useFile);
|
setPrimaryValid(useBase64);
|
BinaryAttributeEditorPanel.this.attrName.setText(attrName);
|
setEnabledOK(true);
|
displayMainPanel();
|
updateEnabling();
|
packParentDialog();
|
if (t != null)
|
{
|
LOG.log(Level.WARNING, "Error reading binary contents: "+t, t);
|
}
|
}
|
};
|
if (launchBackground)
|
{
|
setEnabledOK(false);
|
displayMessage(INFO_CTRL_PANEL_READING_SUMMARY.get());
|
worker.startBackgroundTask();
|
}
|
else
|
{
|
setPrimaryValid(lFile);
|
setPrimaryValid(useFile);
|
setPrimaryValid(useBase64);
|
BinaryAttributeEditorPanel.this.attrName.setText(attrName);
|
setEnabledOK(true);
|
boolean isImage = hasImageSyntax(attrName);
|
setImageVisible(isImage);
|
if (value == null)
|
{
|
if (isImage)
|
{
|
useFile.setSelected(true);
|
}
|
else
|
{
|
useBase64.setSelected(true);
|
}
|
}
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public Component getPreferredFocusComponent()
|
{
|
return file;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void cancelClicked()
|
{
|
valueChanged = false;
|
super.cancelClicked();
|
}
|
|
/**
|
* Returns the binary value displayed in the panel.
|
* @return the binary value displayed in the panel.
|
*/
|
public BinaryValue getBinaryValue()
|
{
|
return value;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void okClicked()
|
{
|
refresh(true, false);
|
}
|
|
/**
|
* Refresh the contents in the panel.
|
* @param closeAndUpdateValue whether the dialog must be closed and the value
|
* updated at the end of the method or not.
|
* @param updateImage whether the displayed image must be updated or not.
|
*/
|
private void refresh(final boolean closeAndUpdateValue,
|
final boolean updateImage)
|
{
|
final ArrayList<Message> errors = new ArrayList<Message>();
|
|
setPrimaryValid(useFile);
|
setPrimaryValid(useBase64);
|
|
final BinaryValue oldValue = value;
|
|
if (closeAndUpdateValue)
|
{
|
value = null;
|
}
|
|
if (useFile.isSelected())
|
{
|
String f = file.getText();
|
if (f.trim().length() == 0)
|
{
|
if (hasImageSyntax(attrName.getText()) && (oldValue != null) &&
|
!updateImage)
|
{
|
// Do nothing. We do not want to regenerate the image and we
|
// are on the case where the user simply did not change the image.
|
}
|
else
|
{
|
errors.add(ERR_CTRL_PANEL_FILE_NOT_PROVIDED.get());
|
setPrimaryInvalid(useFile);
|
setPrimaryInvalid(lFile);
|
}
|
}
|
else
|
{
|
File theFile = new File(f);
|
if (!theFile.exists())
|
{
|
errors.add(ERR_CTRL_PANEL_FILE_DOES_NOT_EXIST.get(f));
|
setPrimaryInvalid(useFile);
|
setPrimaryInvalid(lFile);
|
}
|
else if (theFile.isDirectory())
|
{
|
errors.add(ERR_CTRL_PANEL_PATH_IS_A_DIRECTORY.get(f));
|
setPrimaryInvalid(useFile);
|
setPrimaryInvalid(lFile);
|
}
|
else if (!theFile.canRead())
|
{
|
errors.add(ERR_CTRL_PANEL_CANNOT_READ_FILE.get(f));
|
setPrimaryInvalid(useFile);
|
setPrimaryInvalid(lFile);
|
}
|
}
|
}
|
else
|
{
|
String b = base64.getText();
|
if (b.length() == 0)
|
{
|
errors.add(ERR_CTRL_PANEL_VALUE_IN_BASE_64_REQUIRED.get());
|
setPrimaryInvalid(useBase64);
|
}
|
}
|
if (errors.size() == 0)
|
{
|
// Read the file or encode the base 64 content.
|
BackgroundTask<BinaryValue> worker = new BackgroundTask<BinaryValue>()
|
{
|
/**
|
* {@inheritDoc}
|
*/
|
public BinaryValue processBackgroundTask() throws Throwable
|
{
|
try
|
{
|
Thread.sleep(1000);
|
}
|
catch (Throwable t)
|
{
|
}
|
BinaryValue returnValue;
|
if (useBase64.isSelected())
|
{
|
returnValue = BinaryValue.createBase64(base64.getText());
|
}
|
else if (file.getText().trim().length() > 0)
|
{
|
File f = new File(file.getText());
|
FileInputStream in = null;
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
byte[] bytes = new byte[2 * 1024];
|
try
|
{
|
in = new FileInputStream(f);
|
boolean done = false;
|
while (!done)
|
{
|
int len = in.read(bytes);
|
if (len == -1)
|
{
|
done = true;
|
}
|
else
|
{
|
out.write(bytes, 0, len);
|
}
|
}
|
returnValue = BinaryValue.createFromFile(out.toByteArray(), f);
|
}
|
finally
|
{
|
if (in != null)
|
{
|
in.close();
|
}
|
out.close();
|
}
|
}
|
else
|
{
|
// We do not want to regenerate the image and we
|
// are on the case where the user simply did not change the image.
|
returnValue = oldValue;
|
}
|
if (closeAndUpdateValue)
|
{
|
valueChanged = !returnValue.equals(oldValue);
|
}
|
if (updateImage)
|
{
|
updateImage(lImage, returnValue.getBytes());
|
}
|
return returnValue;
|
}
|
/**
|
* {@inheritDoc}
|
*/
|
public void backgroundTaskCompleted(BinaryValue returnValue,
|
Throwable t)
|
{
|
setEnabledOK(true);
|
displayMainPanel();
|
if (closeAndUpdateValue)
|
{
|
value = returnValue;
|
}
|
else
|
{
|
packParentDialog();
|
}
|
if (t != null)
|
{
|
if (useFile.isSelected())
|
{
|
errors.add(ERR_CTRL_PANEL_ERROR_READING_FILE.get(t.toString()));
|
}
|
else
|
{
|
errors.add(
|
ERR_CTRL_PANEL_ERROR_DECODING_BASE_64.get(t.toString()));
|
}
|
displayErrorDialog(errors);
|
}
|
else
|
{
|
if (closeAndUpdateValue)
|
{
|
Utilities.getParentDialog(BinaryAttributeEditorPanel.this).
|
setVisible(false);
|
}
|
}
|
}
|
};
|
setEnabledOK(false);
|
displayMessage(INFO_CTRL_PANEL_READING_SUMMARY.get());
|
worker.startBackgroundTask();
|
}
|
else
|
{
|
displayErrorDialog(errors);
|
}
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public Message getTitle()
|
{
|
return INFO_CTRL_PANEL_EDIT_BINARY_ATTRIBUTE_TITLE.get();
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public void configurationChanged(ConfigurationChangeEvent ev)
|
{
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public boolean valueChanged()
|
{
|
return valueChanged;
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
public boolean requiresScroll()
|
{
|
return true;
|
}
|
|
/**
|
* Creates the layout of the panel (but the contents are not populated here).
|
*/
|
private void createLayout()
|
{
|
GridBagConstraints gbc = new GridBagConstraints();
|
gbc.gridx = 0;
|
gbc.gridy = 0;
|
gbc.fill = GridBagConstraints.BOTH;
|
gbc.weightx = 0.0;
|
gbc.weighty = 0.0;
|
|
gbc.gridwidth = 1;
|
JLabel l = Utilities.createPrimaryLabel(
|
INFO_CTRL_PANEL_ATTRIBUTE_NAME_LABEL.get());
|
add(l, gbc);
|
gbc.gridx ++;
|
gbc.insets.left = 10;
|
gbc.fill = GridBagConstraints.NONE;
|
gbc.anchor = GridBagConstraints.WEST;
|
attrName = Utilities.createDefaultLabel();
|
gbc.gridwidth = 2;
|
add(attrName, gbc);
|
|
gbc.insets.top = 10;
|
gbc.insets.left = 0;
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
useFile = Utilities.createRadioButton(
|
INFO_CTRL_PANEL_USE_CONTENTS_OF_FILE.get());
|
lFile = Utilities.createPrimaryLabel(
|
INFO_CTRL_PANEL_USE_CONTENTS_OF_FILE.get());
|
useFile.setFont(ColorAndFontConstants.primaryFont);
|
gbc.gridx = 0;
|
gbc.gridy ++;
|
gbc.gridwidth = 1;
|
add(useFile, gbc);
|
add(lFile, gbc);
|
gbc.gridx ++;
|
file = Utilities.createLongTextField();
|
gbc.weightx = 1.0;
|
gbc.insets.left = 10;
|
add(file, gbc);
|
gbc.gridx ++;
|
gbc.weightx = 0.0;
|
browse = Utilities.createButton(INFO_CTRL_PANEL_BROWSE_BUTTON_LABEL.get());
|
browse.addActionListener(
|
new CustomBrowseActionListener(file,
|
BrowseActionListener.BrowseType.OPEN_GENERIC_FILE, this));
|
browse.setOpaque(false);
|
add(browse, gbc);
|
gbc.gridy ++;
|
gbc.gridx = 0;
|
gbc.insets.left = 0;
|
gbc.gridwidth = 3;
|
useBase64 = Utilities.createRadioButton(
|
INFO_CTRL_PANEL_USE_CONTENTS_IN_BASE_64.get());
|
useBase64.setFont(ColorAndFontConstants.primaryFont);
|
add(useBase64, gbc);
|
|
gbc.gridy ++;
|
gbc.insets.left = 30;
|
gbc.fill = GridBagConstraints.BOTH;
|
gbc.weightx = 1.0;
|
base64 = Utilities.createLongTextField();
|
add(base64, gbc);
|
|
imagePreview =
|
Utilities.createPrimaryLabel(INFO_CTRL_PANEL_IMAGE_PREVIEW_LABEL.get());
|
gbc.gridy ++;
|
gbc.gridwidth = 1;
|
gbc.weightx = 0.0;
|
gbc.weighty = 0.0;
|
add(imagePreview, gbc);
|
|
refreshButton = Utilities.createButton(
|
INFO_CTRL_PANEL_REFRESH_BUTTON_LABEL.get());
|
gbc.gridx ++;
|
gbc.insets.left = 5;
|
gbc.fill = GridBagConstraints.NONE;
|
add(refreshButton, gbc);
|
gbc.insets.left = 0;
|
gbc.weightx = 1.0;
|
add(Box.createHorizontalGlue(), gbc);
|
refreshButton.addActionListener(new ActionListener()
|
{
|
/**
|
* {@inheritDoc}
|
*/
|
public void actionPerformed(ActionEvent ev)
|
{
|
refreshButtonClicked();
|
}
|
});
|
|
gbc.gridy ++;
|
gbc.gridwidth = 3;
|
gbc.insets.top = 5;
|
gbc.weightx = 0.0;
|
gbc.weighty = 0.0;
|
add(lImage, gbc);
|
|
addBottomGlue(gbc);
|
ButtonGroup group = new ButtonGroup();
|
group.add(useFile);
|
group.add(useBase64);
|
|
ActionListener listener = new ActionListener()
|
{
|
public void actionPerformed(ActionEvent ev)
|
{
|
updateEnabling();
|
}
|
};
|
useFile.addActionListener(listener);
|
useBase64.addActionListener(listener);
|
}
|
|
/**
|
* Updates the enabling state of all the components in the panel.
|
*
|
*/
|
private void updateEnabling()
|
{
|
base64.setEnabled(useBase64.isSelected());
|
file.setEnabled(useFile.isSelected());
|
browse.setEnabled(useFile.isSelected());
|
refreshButton.setEnabled(useFile.isSelected());
|
}
|
|
/**
|
* Updates the provided component with the base 64 representation of the
|
* provided binary array.
|
* @param base64 the text component to be updated.
|
* @param bytes the byte array.
|
*/
|
static void updateBase64(JTextComponent base64, byte[] bytes)
|
{
|
if (bytes.length < MAX_BASE64_TO_DISPLAY)
|
{
|
BinaryValue value = BinaryValue.createBase64(bytes);
|
base64.setText(value.getBase64());
|
}
|
else
|
{
|
base64.setText(
|
INFO_CTRL_PANEL_SPECIFY_CONTENTS_IN_BASE_64.get().toString());
|
}
|
}
|
|
/**
|
* Updates a label, by displaying the image in the provided byte array.
|
* @param lImage the label to be updated.
|
* @param bytes the array of bytes containing the image.
|
*/
|
static void updateImage(JLabel lImage, byte[] bytes)
|
{
|
Icon icon = Utilities.createImageIcon(bytes,
|
BinaryAttributeEditorPanel.MAX_IMAGE_HEIGHT,
|
INFO_CTRL_PANEL_IMAGE_OF_ATTRIBUTE_LABEL.get(), false);
|
if (icon.getIconHeight() > 0)
|
{
|
lImage.setIcon(icon);
|
lImage.setText("");
|
}
|
else
|
{
|
Utilities.setWarningLabel(lImage,
|
INFO_CTRL_PANEL_PREVIEW_NOT_AVAILABLE_LABEL.get());
|
}
|
}
|
|
/**
|
* Updates the visibility of the components depending on whether the image
|
* must be made visible or not.
|
* @param visible whether the image must be visible or not.
|
*/
|
private void setImageVisible(boolean visible)
|
{
|
imagePreview.setVisible(visible);
|
refreshButton.setVisible(visible);
|
lFile.setVisible(visible);
|
useFile.setVisible(!visible);
|
useBase64.setVisible(!visible);
|
base64.setVisible(!visible);
|
lImage.setVisible(visible);
|
}
|
|
/**
|
* Class used to refresh automatically the contents in the panel after the
|
* user provides a path value through the JFileChooser associated with the
|
* browse button.
|
*
|
*/
|
class CustomBrowseActionListener extends BrowseActionListener
|
{
|
/**
|
* Constructor of this listener.
|
* @param field the text field.
|
* @param type the type of browsing (file, directory, etc.)
|
* @param parent the parent component to be used as reference to display
|
* the file chooser dialog.
|
*/
|
public CustomBrowseActionListener(JTextComponent field, BrowseType type,
|
Component parent)
|
{
|
super(field, type, parent);
|
}
|
|
/**
|
* {@inheritDoc}
|
*/
|
protected void fieldUpdated()
|
{
|
super.fieldUpdated();
|
if (refreshButton.isVisible())
|
{
|
// The file field is updated, if refreshButton is visible it means
|
// that we can have a preview.
|
refreshButtonClicked();
|
}
|
}
|
}
|
|
/**
|
* Called when the refresh button is clicked by the user.
|
*
|
*/
|
private void refreshButtonClicked()
|
{
|
refresh(false, true);
|
}
|
|
/**
|
* Returns <CODE>true</CODE> if the attribute has an image syntax and
|
* <CODE>false</CODE> otherwise.
|
* @param attrName the attribute name.
|
* @return <CODE>true</CODE> if the attribute has an image syntax and
|
* <CODE>false</CODE> otherwise.
|
*/
|
private boolean hasImageSyntax(String attrName)
|
{
|
Schema schema = getInfo().getServerDescriptor().getSchema();
|
return Utilities.hasImageSyntax(attrName, schema);
|
}
|
}
|