com.phloc.css.utils.CSSDataURL Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of phloc-css-jdk5 Show documentation
Show all versions of phloc-css-jdk5 Show documentation
phloc CSS parser an other components
The newest version!
/**
* Copyright (C) 2006-2014 phloc systems
* http://www.phloc.com
* office[at]phloc[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.phloc.css.utils;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.NotThreadSafe;
import org.omg.CORBA_2_3.portable.OutputStream;
import com.phloc.commons.IHasStringRepresentation;
import com.phloc.commons.ValueEnforcer;
import com.phloc.commons.annotations.ReturnsMutableCopy;
import com.phloc.commons.base64.Base64;
import com.phloc.commons.charset.CharsetManager;
import com.phloc.commons.collections.ArrayHelper;
import com.phloc.commons.equals.EqualsUtils;
import com.phloc.commons.hash.HashCodeGenerator;
import com.phloc.commons.mime.CMimeType;
import com.phloc.commons.mime.IMimeType;
import com.phloc.commons.mime.MimeType;
import com.phloc.commons.mime.MimeTypeUtils;
import com.phloc.commons.string.ToStringGenerator;
/**
* This class represents a single CSS data URL (RFC 2397).
* Note: manual serialization is required, because {@link Charset} is not
* Serializable.
*
* @author Philip Helger
*/
@NotThreadSafe
public class CSSDataURL implements IHasStringRepresentation, Serializable
{
private IMimeType m_aMimeType;
private boolean m_bBase64Encoded;
private byte [] m_aContent;
private Charset m_aCharset;
private String m_sContent;
/**
* Determine the charset from the passed MIME type. If no charset was found,
* return the default charset.
*
* @param aMimeType
* The MIME type to investigate.
* @return Never null
.
*/
@Nonnull
public static Charset getCharsetFromMimeTypeOrDefault (@Nullable final IMimeType aMimeType)
{
final Charset ret = MimeTypeUtils.getCharsetFromMimeType (aMimeType);
return ret != null ? ret : CSSDataURLHelper.DEFAULT_CHARSET;
}
/**
* Default constructor. Default MIME type, no Base64 encoding and no content.
*/
public CSSDataURL ()
{
this (CSSDataURLHelper.DEFAULT_MIME_TYPE.getClone (), false, new byte [0], CSSDataURLHelper.DEFAULT_CHARSET, "");
}
/**
* Constructor
*
* @param aMimeType
* The MIME type to be used. If it contains a charset, this charset
* will be used otherwise the default charset will be used.
* @param bBase64Encoded
* true
if the content of this data should be Base64
* encoded. It is recommended to set this to true
if you
* have binary data like images.
* @param aContent
* The content od the data URL as a byte array. May not be
* null
.
*/
public CSSDataURL (@Nonnull final IMimeType aMimeType, final boolean bBase64Encoded, @Nonnull final byte [] aContent)
{
this (aMimeType, bBase64Encoded, aContent, getCharsetFromMimeTypeOrDefault (aMimeType), null);
}
/**
* Full constructor
*
* @param aMimeType
* The MIME type to be used. May not be null
. If you don't
* know provide the default MIME type from
* {@link CSSDataURLHelper#DEFAULT_MIME_TYPE}.
* @param bBase64Encoded
* true
if the data URL String representation should be
* Base64 encoded, false
if not. It is recommended to set
* this to true
if you have binary data like images.
* @param aContent
* The content of the data URL as a byte array. May not be
* null
but may be empty. This content may not be Base64
* encoded!
* @param aCharset
* The charset to be used to encode the String. May not be
* null
. The default is
* {@link CSSDataURLHelper#DEFAULT_CHARSET}.
* @param sContent
* The String representation of the content. It must match the byte
* array in the specified charset. If this parameter is
* null
than the String content representation is lazily
* created in {@link #getContentAsString()}.
*/
public CSSDataURL (@Nonnull final IMimeType aMimeType,
final boolean bBase64Encoded,
@Nonnull final byte [] aContent,
@Nonnull final Charset aCharset,
@Nullable final String sContent)
{
ValueEnforcer.notNull (aMimeType, "MimeType");
ValueEnforcer.notNull (aContent, "Content");
ValueEnforcer.notNull (aCharset, "Charset");
// Check if a charset is contained in the MIME type and if it matches the
// provided charset
final Charset aMimeTypeCharset = MimeTypeUtils.getCharsetFromMimeType (aMimeType);
if (aMimeTypeCharset == null)
{
// No charset found in MIME type
if (!aCharset.equals (CSSDataURLHelper.DEFAULT_CHARSET))
{
// append charset only if it is not the default charset
m_aMimeType = ((MimeType) aMimeType.getClone ()).addParameter (CMimeType.PARAMETER_NAME_CHARSET,
aCharset.name ());
}
else
{
// Default charset provided
m_aMimeType = aMimeType;
}
}
else
{
// MIME type has a charset - check if it matches the passed one
if (!aMimeTypeCharset.equals (aCharset))
throw new IllegalArgumentException ("The provided charset '" +
aCharset.name () +
"' differs from the charset in the MIME type: '" +
aMimeTypeCharset.name () +
"'");
m_aMimeType = aMimeType;
}
m_bBase64Encoded = bBase64Encoded;
m_aContent = ArrayHelper.getCopy (aContent);
m_aCharset = aCharset;
m_sContent = sContent;
}
private void writeObject (@Nonnull final ObjectOutputStream out) throws IOException
{
out.writeObject (m_aMimeType);
out.writeBoolean (m_bBase64Encoded);
out.writeObject (m_aContent);
out.writeUTF (m_aCharset.name ());
out.writeObject (m_sContent);
}
private void readObject (@Nonnull final ObjectInputStream in) throws IOException, ClassNotFoundException
{
m_aMimeType = (IMimeType) in.readObject ();
m_bBase64Encoded = in.readBoolean ();
m_aContent = (byte []) in.readObject ();
final String sCharsetName = in.readUTF ();
m_aCharset = CharsetManager.getCharsetFromName (sCharsetName);
m_sContent = (String) in.readObject ();
}
/**
* @return The MIME type of the data URL. If none was specified, than the
* default MIME Type {@link CSSDataURLHelper#DEFAULT_MIME_TYPE} must
* be used.
*/
@Nonnull
public IMimeType getMimeType ()
{
return m_aMimeType;
}
/**
* @return true
if the parsed data URL was Base64 encoded or if
* this data URL should be Base64 encoded.
*/
public boolean isBase64Encoded ()
{
return m_bBase64Encoded;
}
/**
* @return The length of the content in bytes. Always ≥ 0.
*/
@Nonnegative
public int getContentLength ()
{
return m_aContent.length;
}
/**
* Get a copy of all content bytes. No Base64 encoding is performed in this
* method.
*
* @return A copy of the binary data of the data URL. Neither
* null
but maybe empty.
*/
@Nonnull
@ReturnsMutableCopy
public byte [] getContentBytes ()
{
return ArrayHelper.getCopy (m_aContent);
}
/**
* Write all the binary content to the passed output stream. No Base64
* encoding is performed in this method.
*
* @param aOS
* The output stream to write to. May not be null
.
* @throws IOException
*/
public void writeContentBytes (@Nonnull @WillNotClose final OutputStream aOS) throws IOException
{
aOS.write (m_aContent, 0, m_aContent.length);
}
/**
* @return The charset to be used for String encoding. May not be
* null
. The default is
* {@link CSSDataURLHelper#DEFAULT_CHARSET}.
*/
@Nonnull
public Charset getCharset ()
{
return m_aCharset;
}
/**
* Get the data content of this Data URL as String. If no String
* representation was provided in the constructor, than it is lazily created
* inside this method in which case instances of this class are not
* thread-safe. If a non-null
String was provided in the
* constructor, this object is immutable. No Base64 encoding is performed in
* this method.
*
* @return The content in a String representation using the charset of this
* object. Never null
.
*/
@Nonnull
public String getContentAsString ()
{
if (m_sContent == null)
m_sContent = CharsetManager.getAsString (m_aContent, m_aCharset);
return m_sContent;
}
/**
* Get the content as a Base64 encoded {@link String} in the {@link Charset}
* specified by {@link #getCharset()}. The encoding is applied independent of
* the {@link #isBase64Encoded()} state.
*
* @return Never null
.
*/
@Nonnull
public String getContentAsBase64EncodedString ()
{
// Add Base64 encoded String
final byte [] aEncoded = Base64.encodeBytesToBytes (m_aContent);
// Print the string in the specified charset
return CharsetManager.getAsString (aEncoded, m_aCharset);
}
/**
* Get the data content of this Data URL as String in the specified charset.
* No Base64 encoding is performed in this method.
*
* @param aCharset
* The charset to be used. May not be null
.
* @return The content in a String representation using the provided charset.
* Never null
.
*/
@Nonnull
public String getContentAsString (@Nonnull final Charset aCharset)
{
return CharsetManager.getAsString (m_aContent, aCharset);
}
/**
* @return The complete representation of the data URL, starting with "data:".
* All data is emitted, even if it is the default value. Base64
* encoding is performed in this method.
*/
@Nonnull
public String getAsString ()
{
// Return the non-optimized version
return getAsString (false);
}
/**
* @return The complete representation of the data URL, starting with "data:".
* All data is emitted, even if it is the default value.
*/
@Nonnull
public String getAsString (final boolean bOptimizedVersion)
{
final StringBuilder aSB = new StringBuilder (CSSDataURLHelper.PREFIX_DATA_URL);
if (bOptimizedVersion)
{
// Do not emit the default, if it is the optimized version
if (!m_aMimeType.equals (CSSDataURLHelper.DEFAULT_MIME_TYPE))
if (m_aMimeType.getAsStringWithoutParameters ()
.equals (CSSDataURLHelper.DEFAULT_MIME_TYPE.getAsStringWithoutParameters ()))
{
// Emit only the parameters
aSB.append (m_aMimeType.getParametersAsString (CSSDataURLHelper.MIME_QUOTING));
}
else
{
// Non-default MIME type
aSB.append (m_aMimeType.getAsString (CSSDataURLHelper.MIME_QUOTING));
}
}
else
{
// Use URL escaping to quote MIME type parameter values!
aSB.append (m_aMimeType.getAsString (CSSDataURLHelper.MIME_QUOTING));
}
// Base64 marker
if (m_bBase64Encoded)
{
// Avoid the ";base64" if the content is empty
if (m_aContent.length > 0 || !bOptimizedVersion)
aSB.append (CSSDataURLHelper.BASE64_MARKER);
}
// Start content
aSB.append (CSSDataURLHelper.SEPARATOR_CONTENT);
if (m_aContent.length > 0)
{
if (m_bBase64Encoded)
{
// Add Base64 encoded String
aSB.append (getContentAsBase64EncodedString ());
}
else
{
// Append String as is
aSB.append (getContentAsString ());
}
}
return aSB.toString ();
}
@Override
public boolean equals (final Object o)
{
if (o == this)
return true;
if (o == null || !getClass ().equals (o.getClass ()))
return false;
final CSSDataURL rhs = (CSSDataURL) o;
return m_aMimeType.equals (rhs.m_aMimeType) &&
m_bBase64Encoded == rhs.m_bBase64Encoded &&
EqualsUtils.equals (m_aContent, rhs.m_aContent) &&
m_aCharset.equals (rhs.m_aCharset);
}
@Override
public int hashCode ()
{
return new HashCodeGenerator (this).append (m_aMimeType)
.append (m_bBase64Encoded)
.append (m_aContent)
.append (m_aCharset)
.getHashCode ();
}
@Override
public String toString ()
{
return new ToStringGenerator (this).append ("mimeType", m_aMimeType)
.append ("base64Encoded", m_bBase64Encoded)
.append ("content.length", m_aContent.length)
.append ("charset", m_aCharset)
.append ("hasStringContent", m_sContent != null)
.toString ();
}
}