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-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS
026 */
027
028package org.opends.guitools.controlpanel.datamodel;
029
030import java.io.File;
031import java.text.ParseException;
032import java.util.Objects;
033
034import org.opends.server.util.Base64;
035
036/**
037 * Class used to represent Binary Values.  This is required in particular
038 * when the user wants to use the value in a file.  To be able to reflect
039 * this this object is used: it contains the binary itself, the base 64
040 * representation and the file that has been used.
041 *
042 */
043public class BinaryValue
044{
045  private Type type;
046  private String base64;
047  private byte[] bytes;
048  private File file;
049  private int hashCode;
050
051  /**
052   * The type of the binary value.
053   *
054   */
055  public enum Type
056  {
057    /**
058     * The binary value is provided as Base 64 string.
059     */
060    BASE64_STRING,
061    /**
062     * The binary value is provided as a byte array.
063     */
064    BYTE_ARRAY
065  }
066
067  /**
068   * This is done to force the use of the factory methods (createBase64 and
069   * createFromFile).
070   *
071   */
072  private BinaryValue()
073  {
074  }
075
076  /**
077   * Creates a binary value using a base64 string.
078   * @param base64 the base64 representation of the binary.
079   * @return the binary value.
080   * @throws ParseException if there is an error decoding the provided base64
081   * string.
082   */
083  public static BinaryValue createBase64(String base64) throws ParseException
084  {
085    BinaryValue value =  new BinaryValue();
086    value.type = Type.BASE64_STRING;
087    value.base64 = base64;
088    value.bytes = value.getBytes();
089    value.hashCode = base64.hashCode();
090    return value;
091  }
092
093  /**
094   * Creates a binary value using an array of bytes.
095   * @param bytes the byte array.
096   * @return the binary value.
097   */
098  public static BinaryValue createBase64(byte[] bytes)
099  {
100    BinaryValue value =  new BinaryValue();
101    value.type = Type.BASE64_STRING;
102    value.bytes = bytes;
103    value.base64 = value.getBase64();
104    value.hashCode = value.base64.hashCode();
105    return value;
106  }
107
108  /**
109   * Creates a binary value using an array of bytes and a file.
110   * @param bytes the bytes in the file.
111   * @param file the file the bytes were read from.
112   * @return the binary value.
113   */
114  public static BinaryValue createFromFile(byte[] bytes, File file)
115  {
116    BinaryValue value =  new BinaryValue();
117    value.type = Type.BYTE_ARRAY;
118    value.bytes = bytes;
119    value.base64 = value.getBase64();
120    value.hashCode = value.base64.hashCode();
121    value.file = file;
122    return value;
123  }
124
125  /**
126   * Returns the base64 representation of the binary value.
127   * @return the base64 representation of the binary value.
128   */
129  public String getBase64()
130  {
131    if (base64 == null && bytes != null)
132    {
133      base64 = Base64.encode(bytes);
134    }
135    return base64;
136  }
137
138  /**
139   * Returns the byte array of the binary value.
140   * @return the byte array of the binary value.
141   * @throws ParseException if this object was created using a base64 string
142   * and there was an error parsing it.
143   */
144  public byte[] getBytes() throws ParseException
145  {
146    if (bytes == null && base64 != null)
147    {
148      bytes = Base64.decode(base64);
149    }
150    return bytes;
151  }
152
153  /**
154   * Return the type of the binary value.
155   * @return the type of the binary value.
156   */
157  public Type getType()
158  {
159    return type;
160  }
161
162  /**
163   * Return the file that was used to read the binary value.
164   * @return the file that was used to read the binary value.
165   */
166  public File getFile()
167  {
168    return file;
169  }
170
171  /** {@inheritDoc} */
172  public boolean equals(Object o)
173  {
174    if (this == o)
175    {
176      return true;
177    }
178    if (o instanceof BinaryValue)
179    {
180      BinaryValue candidate = (BinaryValue)o;
181      return candidate.getType() == getType()
182          && Objects.equals(file, candidate.getFile())
183          && bytesEqual(candidate);
184    }
185    return false;
186  }
187
188  private boolean bytesEqual(BinaryValue candidate)
189  {
190    if (type == Type.BASE64_STRING)
191    {
192      return candidate.getBase64().equals(getBase64());
193    }
194
195    try
196    {
197      if (candidate.getBytes().length != getBytes().length) {
198        return false;
199      }
200      boolean equals = true;
201      for (int i=0; i<getBytes().length && equals; i++)
202      {
203        equals = bytes[i] == candidate.getBytes()[i];
204      }
205      return equals;
206    }
207    catch (ParseException pe)
208    {
209      throw new RuntimeException(
210          "Unexpected error getting bytes: "+pe, pe);
211    }
212  }
213
214  /** {@inheritDoc} */
215  public int hashCode()
216  {
217    return hashCode;
218  }
219}