michael@0: /** michael@0: * Copyright (c) 2012, Ben Fortuna michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * michael@0: * o Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * michael@0: * o Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * o Neither the name of Ben Fortuna nor the names of any other contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF michael@0: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING michael@0: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS michael@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: package net.fortuna.ical4j.model.property; michael@0: michael@0: import java.io.IOException; michael@0: import java.io.UnsupportedEncodingException; michael@0: import java.net.URI; michael@0: import java.net.URISyntaxException; michael@0: michael@0: import net.fortuna.ical4j.model.Parameter; michael@0: import net.fortuna.ical4j.model.ParameterList; michael@0: import net.fortuna.ical4j.model.Property; michael@0: import net.fortuna.ical4j.model.PropertyFactoryImpl; michael@0: import net.fortuna.ical4j.model.ValidationException; michael@0: import net.fortuna.ical4j.model.parameter.Encoding; michael@0: import net.fortuna.ical4j.model.parameter.Value; michael@0: import net.fortuna.ical4j.util.DecoderFactory; michael@0: import net.fortuna.ical4j.util.EncoderFactory; michael@0: import net.fortuna.ical4j.util.ParameterValidator; michael@0: import net.fortuna.ical4j.util.Strings; michael@0: import net.fortuna.ical4j.util.Uris; michael@0: michael@0: import org.apache.commons.codec.BinaryDecoder; michael@0: import org.apache.commons.codec.BinaryEncoder; michael@0: import org.apache.commons.codec.DecoderException; michael@0: import org.apache.commons.codec.EncoderException; michael@0: import org.apache.commons.logging.Log; michael@0: import org.apache.commons.logging.LogFactory; michael@0: michael@0: /** michael@0: * $Id$ michael@0: * michael@0: * Created: [Apr 6, 2004] michael@0: * michael@0: * Defines an ATTACH iCalendar component property. michael@0: * michael@0: *
michael@0:  *       4.8.1.1 Attachment
michael@0:  *       
michael@0:  *          Property Name: ATTACH
michael@0:  *       
michael@0:  *          Purpose: The property provides the capability to associate a document
michael@0:  *          object with a calendar component.
michael@0:  *       
michael@0:  *          Value Type: The default value type for this property is URI. The
michael@0:  *          value type can also be set to BINARY to indicate inline binary
michael@0:  *          encoded content information.
michael@0:  *       
michael@0:  *          Property Parameters: Non-standard, inline encoding, format type and
michael@0:  *          value data type property parameters can be specified on this
michael@0:  *          property.
michael@0:  *        
michael@0:  *          Conformance: The property can be specified in a "VEVENT", "VTODO",
michael@0:  *          "VJOURNAL" or "VALARM" calendar components.
michael@0:  *       
michael@0:  *          Description: The property can be specified within "VEVENT", "VTODO",
michael@0:  *          "VJOURNAL", or "VALARM" calendar components. This property can be
michael@0:  *          specified multiple times within an iCalendar object.
michael@0:  *       
michael@0:  *          Format Definition: The property is defined by the following notation:
michael@0:  *       
michael@0:  *            attach     = "ATTACH" attparam ":" uri  CRLF
michael@0:  *       
michael@0:  *        attach     =/ "ATTACH" attparam ";" "ENCODING" "=" "BASE64"
michael@0:  *                          ";" "VALUE" "=" "BINARY" ":" binary
michael@0:  *       
michael@0:  *            attparam   = *(
michael@0:  *       
michael@0:  *                       ; the following is optional,
michael@0:  *                       ; but MUST NOT occur more than once
michael@0:  *       
michael@0:  *                       (";" fmttypeparam) /
michael@0:  *       
michael@0:  *                       ; the following is optional,
michael@0:  *                       ; and MAY occur more than once
michael@0:  *       
michael@0:  *                       (";" xparam)
michael@0:  *       
michael@0:  *                       )
michael@0:  * 
michael@0: * michael@0: * @author benf michael@0: */ michael@0: public class Attach extends Property { michael@0: michael@0: private static final long serialVersionUID = 4439949507756383452L; michael@0: michael@0: private URI uri; michael@0: michael@0: private byte[] binary; michael@0: michael@0: /** michael@0: * Default constructor. michael@0: */ michael@0: public Attach() { michael@0: super(ATTACH, PropertyFactoryImpl.getInstance()); michael@0: } michael@0: michael@0: /** michael@0: * @param aList a list of parameters for this component michael@0: * @param aValue a value string for this component michael@0: * @throws IOException when there is an error reading the binary stream michael@0: * @throws URISyntaxException where the specified string is not a valid uri michael@0: */ michael@0: public Attach(final ParameterList aList, final String aValue) michael@0: throws IOException, URISyntaxException { michael@0: super(ATTACH, aList, PropertyFactoryImpl.getInstance()); michael@0: setValue(aValue); michael@0: } michael@0: michael@0: /** michael@0: * @param data binary data michael@0: */ michael@0: public Attach(final byte[] data) { michael@0: super(ATTACH, PropertyFactoryImpl.getInstance()); michael@0: // add required parameters.. michael@0: getParameters().add(Encoding.BASE64); michael@0: getParameters().add(Value.BINARY); michael@0: this.binary = data; michael@0: } michael@0: michael@0: /** michael@0: * @param aList a list of parameters for this component michael@0: * @param data binary data michael@0: */ michael@0: public Attach(final ParameterList aList, final byte[] data) { michael@0: super(ATTACH, aList, PropertyFactoryImpl.getInstance()); michael@0: this.binary = data; michael@0: } michael@0: michael@0: /** michael@0: * @param aUri a URI michael@0: */ michael@0: public Attach(final URI aUri) { michael@0: super(ATTACH, PropertyFactoryImpl.getInstance()); michael@0: this.uri = aUri; michael@0: } michael@0: michael@0: /** michael@0: * @param aList a list of parameters for this component michael@0: * @param aUri a URI michael@0: */ michael@0: public Attach(final ParameterList aList, final URI aUri) { michael@0: super(ATTACH, aList, PropertyFactoryImpl.getInstance()); michael@0: this.uri = aUri; michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final void validate() throws ValidationException { michael@0: michael@0: /* michael@0: * ; the following is optional, ; but MUST NOT occur more than once (";" fmttypeparam) / michael@0: */ michael@0: ParameterValidator.getInstance().assertOneOrLess(Parameter.FMTTYPE, michael@0: getParameters()); michael@0: michael@0: /* michael@0: * ; the following is optional, ; and MAY occur more than once (";" xparam) michael@0: */ michael@0: michael@0: /* michael@0: * If the value type parameter is ";VALUE=BINARY", then the inline encoding parameter MUST be specified with the michael@0: * value ";ENCODING=BASE64". michael@0: */ michael@0: if (Value.BINARY.equals(getParameter(Parameter.VALUE))) { michael@0: ParameterValidator.getInstance().assertOne(Parameter.ENCODING, michael@0: getParameters()); michael@0: if (!Encoding.BASE64.equals(getParameter(Parameter.ENCODING))) { michael@0: throw new ValidationException( michael@0: "If the value type parameter is [BINARY], the inline" michael@0: + "encoding parameter MUST be specified with the value [BASE64]"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * @return Returns the binary. michael@0: */ michael@0: public final byte[] getBinary() { michael@0: return binary; michael@0: } michael@0: michael@0: /** michael@0: * @return Returns the uri. michael@0: */ michael@0: public final URI getUri() { michael@0: return uri; michael@0: } michael@0: michael@0: /** michael@0: * Sets the current value of the Attach instance. If the specified michael@0: * value is encoded binary data, the value is decoded and stored in michael@0: * the binary field. Otherwise the value is assumed to be a URI michael@0: * location to binary data and is stored as such. michael@0: * michael@0: * @param aValue a string encoded binary or URI value michael@0: * @throws IOException where binary data cannot be decoded michael@0: * @throws URISyntaxException where the specified value is not a valid URI michael@0: */ michael@0: public final void setValue(final String aValue) throws IOException, michael@0: URISyntaxException { michael@0: michael@0: // determine if ATTACH is a URI or an embedded michael@0: // binary.. michael@0: if (getParameter(Parameter.ENCODING) != null) { michael@0: // binary = Base64.decode(aValue); michael@0: try { michael@0: final BinaryDecoder decoder = DecoderFactory.getInstance() michael@0: .createBinaryDecoder( michael@0: (Encoding) getParameter(Parameter.ENCODING)); michael@0: binary = decoder.decode(aValue.getBytes()); michael@0: } michael@0: catch (UnsupportedEncodingException uee) { michael@0: Log log = LogFactory.getLog(Attach.class); michael@0: log.error("Error encoding binary data", uee); michael@0: } michael@0: catch (DecoderException de) { michael@0: Log log = LogFactory.getLog(Attach.class); michael@0: log.error("Error decoding binary data", de); michael@0: } michael@0: } michael@0: // assume URI.. michael@0: else { michael@0: uri = Uris.create(aValue); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: public final String getValue() { michael@0: if (getUri() != null) { michael@0: return Uris.decode(Strings.valueOf(getUri())); michael@0: } michael@0: else if (getBinary() != null) { michael@0: // return Base64.encodeBytes(getBinary(), Base64.DONT_BREAK_LINES); michael@0: try { michael@0: final BinaryEncoder encoder = EncoderFactory.getInstance() michael@0: .createBinaryEncoder( michael@0: (Encoding) getParameter(Parameter.ENCODING)); michael@0: return new String(encoder.encode(getBinary())); michael@0: } michael@0: catch (UnsupportedEncodingException uee) { michael@0: Log log = LogFactory.getLog(Attach.class); michael@0: log.error("Error encoding binary data", uee); michael@0: } michael@0: catch (EncoderException ee) { michael@0: Log log = LogFactory.getLog(Attach.class); michael@0: log.error("Error encoding binary data", ee); michael@0: } michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: /** michael@0: * @param binary The binary to set. michael@0: */ michael@0: public final void setBinary(final byte[] binary) { michael@0: this.binary = binary; michael@0: // unset uri.. michael@0: this.uri = null; michael@0: } michael@0: michael@0: /** michael@0: * @param uri The uri to set. michael@0: */ michael@0: public final void setUri(final URI uri) { michael@0: this.uri = uri; michael@0: // unset binary.. michael@0: this.binary = null; michael@0: } michael@0: }