001package ezvcard.property;
002
003import java.io.BufferedInputStream;
004import java.io.IOException;
005import java.io.InputStream;
006import java.nio.file.Files;
007import java.nio.file.Path;
008import java.util.Arrays;
009import java.util.LinkedHashMap;
010import java.util.List;
011import java.util.Map;
012
013import ezvcard.VCard;
014import ezvcard.VCardVersion;
015import ezvcard.ValidationWarning;
016import ezvcard.parameter.MediaTypeParameter;
017import ezvcard.parameter.Pid;
018import ezvcard.util.Gobble;
019
020/*
021 Copyright (c) 2012-2023, Michael Angstadt
022 All rights reserved.
023
024 Redistribution and use in source and binary forms, with or without
025 modification, are permitted provided that the following conditions are met: 
026
027 1. Redistributions of source code must retain the above copyright notice, this
028 list of conditions and the following disclaimer. 
029 2. Redistributions in binary form must reproduce the above copyright notice,
030 this list of conditions and the following disclaimer in the documentation
031 and/or other materials provided with the distribution. 
032
033 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
034 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
035 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
037 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
039 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
040 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
041 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
042 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
043
044 The views and conclusions contained in the software and documentation are those
045 of the authors and should not be interpreted as representing official policies, 
046 either expressed or implied, of the FreeBSD Project.
047 */
048
049/**
050 * Represents a property whose value contains binary data.
051 * @author Michael Angstadt
052 * @param <T> the class used for representing the content type of the resource
053 */
054public abstract class BinaryProperty<T extends MediaTypeParameter> extends VCardProperty implements HasAltId {
055        /**
056         * The decoded data.
057         */
058        protected byte[] data;
059
060        /**
061         * The URL to the resource.
062         */
063        protected String url;
064
065        /**
066         * The content type of the resource (for example, a JPEG image).
067         */
068        protected T contentType;
069
070        public BinaryProperty() {
071                //empty
072        }
073
074        /**
075         * Creates a binary property.
076         * @param url the URL to the resource
077         * @param type the content type
078         */
079        public BinaryProperty(String url, T type) {
080                setUrl(url, type);
081        }
082
083        /**
084         * Creates a binary property.
085         * @param data the binary data
086         * @param type the content type
087         */
088        public BinaryProperty(byte[] data, T type) {
089                setData(data, type);
090        }
091
092        /**
093         * Creates a binary property.
094         * @param in an input stream to the binary data (will be closed)
095         * @param type the content type
096         * @throws IOException if there is a problem reading from the input stream
097         */
098        public BinaryProperty(InputStream in, T type) throws IOException {
099                this(new Gobble(in).asByteArray(), type);
100        }
101
102        /**
103         * Creates a binary property.
104         * @param file the file containing the binary data
105         * @param type the content type
106         * @throws IOException if there is a problem reading from the file
107         */
108        public BinaryProperty(Path file, T type) throws IOException {
109                this(new BufferedInputStream(Files.newInputStream(file)), type);
110        }
111
112        /**
113         * Copy constructor.
114         * @param original the property to make a copy of
115         */
116        public BinaryProperty(BinaryProperty<T> original) {
117                super(original);
118                data = (original.data == null) ? null : original.data.clone();
119                url = original.url;
120                contentType = original.contentType;
121        }
122
123        /**
124         * Gets the binary data of the resource.
125         * @return the binary data or null if there is none
126         */
127        public byte[] getData() {
128                return data;
129        }
130
131        /**
132         * Sets the binary data of the resource.
133         * @param data the binary data
134         * @param type the content type (e.g. "JPEG image")
135         */
136        public void setData(byte[] data, T type) {
137                this.url = null;
138                this.data = data;
139                setContentType(type);
140        }
141
142        /**
143         * Gets the URL to the resource
144         * @return the URL or null if there is none
145         */
146        public String getUrl() {
147                return url;
148        }
149
150        /**
151         * Sets the URL to the resource.
152         * @param url the URL
153         * @param type the content type (e.g. "JPEG image")
154         */
155        public void setUrl(String url, T type) {
156                this.url = url;
157                this.data = null;
158                setContentType(type);
159        }
160
161        /**
162         * Gets the content type of the resource.
163         * @return the content type (e.g. "JPEG image")
164         */
165        public T getContentType() {
166                return contentType;
167        }
168
169        /**
170         * Sets the content type of the resource.
171         * @param contentType the content type (e.g. "JPEG image")
172         */
173        public void setContentType(T contentType) {
174                this.contentType = contentType;
175        }
176
177        /**
178         * Gets the vCard 4.0 TYPE parameter. This should NOT be used to get the
179         * TYPE parameter for 2.1/3.0 vCards. Use {@link #getContentType} instead.
180         * <p>
181         * <b>Supported versions:</b> {@code 4.0}
182         * </p>
183         * @return the TYPE value (typically, this will be either "work" or "home")
184         * or null if it doesn't exist
185         */
186        public String getType() {
187                return parameters.getType();
188        }
189
190        /**
191         * Sets the vCard 4.0 TYPE parameter. This should NOT be used to set the
192         * TYPE parameter for 2.1/3.0 vCards. Use {@link #setContentType} instead.
193         * <p>
194         * <b>Supported versions:</b> {@code 4.0}
195         * </p>
196         * @param type the TYPE value (should be either "work" or "home") or null to
197         * remove
198         */
199        public void setType(String type) {
200                parameters.setType(type);
201        }
202
203        @Override
204        public List<Pid> getPids() {
205                return super.getPids();
206        }
207
208        @Override
209        public Integer getPref() {
210                return super.getPref();
211        }
212
213        @Override
214        public void setPref(Integer pref) {
215                super.setPref(pref);
216        }
217
218        //@Override
219        public String getAltId() {
220                return parameters.getAltId();
221        }
222
223        //@Override
224        public void setAltId(String altId) {
225                parameters.setAltId(altId);
226        }
227
228        @Override
229        protected void _validate(List<ValidationWarning> warnings, VCardVersion version, VCard vcard) {
230                if (url == null && data == null) {
231                        warnings.add(new ValidationWarning(8));
232                }
233        }
234
235        @Override
236        protected Map<String, Object> toStringValues() {
237                Map<String, Object> values = new LinkedHashMap<>();
238                values.put("data", (data == null) ? "null" : "length: " + data.length);
239                values.put("url", url);
240                values.put("contentType", contentType);
241                return values;
242        }
243
244        @Override
245        public int hashCode() {
246                final int prime = 31;
247                int result = super.hashCode();
248                result = prime * result + ((contentType == null) ? 0 : contentType.hashCode());
249                result = prime * result + Arrays.hashCode(data);
250                result = prime * result + ((url == null) ? 0 : url.hashCode());
251                return result;
252        }
253
254        @Override
255        public boolean equals(Object obj) {
256                if (this == obj) return true;
257                if (!super.equals(obj)) return false;
258                BinaryProperty<?> other = (BinaryProperty<?>) obj;
259                if (contentType == null) {
260                        if (other.contentType != null) return false;
261                } else if (!contentType.equals(other.contentType)) return false;
262                if (!Arrays.equals(data, other.data)) return false;
263                if (url == null) {
264                        if (other.url != null) return false;
265                } else if (!url.equals(other.url)) return false;
266                return true;
267        }
268}