001package ezvcard;
002
003import static ezvcard.VCardVersion.V2_1;
004import static ezvcard.VCardVersion.V3_0;
005import static ezvcard.VCardVersion.V4_0;
006
007import java.lang.reflect.Field;
008import java.lang.reflect.Modifier;
009import java.util.Collection;
010
011import ezvcard.util.CaseClasses;
012
013/*
014 Copyright (c) 2012-2023, Michael Angstadt
015 All rights reserved.
016
017 Redistribution and use in source and binary forms, with or without
018 modification, are permitted provided that the following conditions are met: 
019
020 1. Redistributions of source code must retain the above copyright notice, this
021 list of conditions and the following disclaimer. 
022 2. Redistributions in binary form must reproduce the above copyright notice,
023 this list of conditions and the following disclaimer in the documentation
024 and/or other materials provided with the distribution. 
025
026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
029 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
030 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
031 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
032 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
033 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
034 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036
037 The views and conclusions contained in the software and documentation are those
038 of the authors and should not be interpreted as representing official policies, 
039 either expressed or implied, of the FreeBSD Project.
040 */
041
042/**
043 * Defines the data type of a property's value.
044 * @author Michael Angstadt
045 */
046public class VCardDataType {
047        private static final CaseClasses<VCardDataType, String> enums = new CaseClasses<VCardDataType, String>(VCardDataType.class) {
048                @Override
049                protected VCardDataType create(String value) {
050                        return new VCardDataType(value);
051                }
052
053                @Override
054                protected boolean matches(VCardDataType dataType, String value) {
055                        return dataType.name.equalsIgnoreCase(value);
056                }
057        };
058
059        /**
060         * A uniform resource locator (for example,
061         * "http://www.example.com/image.jpg"). This data type is only used in 2.1
062         * vCards. All other vCard versions use {@link #URI}.
063         */
064        @SupportedVersions(V2_1)
065        public static final VCardDataType URL = new VCardDataType("url");
066
067        /**
068         * Refers to a MIME entity within an email.
069         */
070        @SupportedVersions(V2_1)
071        public static final VCardDataType CONTENT_ID = new VCardDataType("content-id");
072
073        /**
074         * A non-textual value, such as a picture or sound file.
075         */
076        @SupportedVersions(V3_0)
077        public static final VCardDataType BINARY = new VCardDataType("binary");
078
079        /**
080         * A uniform resource identifier (for example,
081         * "http://www.example.com/image.jpg"). 2.1 vCards use {@link #URL} instead.
082         */
083        @SupportedVersions({ V3_0, V4_0 })
084        public static final VCardDataType URI = new VCardDataType("uri");
085
086        /**
087         * A plain text value.
088         */
089        public static final VCardDataType TEXT = new VCardDataType("text");
090
091        /**
092         * A date that does not have a time component (for example, "2015-02-16").
093         */
094        @SupportedVersions({ V3_0, V4_0 })
095        public static final VCardDataType DATE = new VCardDataType("date");
096
097        /**
098         * A time that does not have a date component (for example, "08:34:00").
099         */
100        @SupportedVersions({ V3_0, V4_0 })
101        public static final VCardDataType TIME = new VCardDataType("time");
102
103        /**
104         * A date with a time component (for example, "2015-02-16 08:34:00").
105         */
106        @SupportedVersions({ V3_0, V4_0 })
107        public static final VCardDataType DATE_TIME = new VCardDataType("date-time");
108
109        /**
110         * Any sort of date/time combination. The value can be a date (e.g.
111         * "2015-02-16"), a time (e.g. "08:34:00"), or a date with a time component
112         * (e.g. "2015-02-16 08:34:00").
113         */
114        @SupportedVersions(V4_0)
115        public static final VCardDataType DATE_AND_OR_TIME = new VCardDataType("date-and-or-time");
116
117        /**
118         * A specific moment in time. Timestamps should be in UTC time.
119         */
120        @SupportedVersions(V4_0)
121        public static final VCardDataType TIMESTAMP = new VCardDataType("timestamp");
122
123        /**
124         * A boolean value ("true" or "false").
125         */
126        @SupportedVersions(V4_0)
127        public static final VCardDataType BOOLEAN = new VCardDataType("boolean");
128
129        /**
130         * An integer value (for example, "42").
131         */
132        @SupportedVersions(V4_0)
133        public static final VCardDataType INTEGER = new VCardDataType("integer");
134
135        /**
136         * A floating-point value (for example, "3.14").
137         */
138        @SupportedVersions(V4_0)
139        public static final VCardDataType FLOAT = new VCardDataType("float");
140
141        /**
142         * An offset from UTC time, in hours and minutes (for example, "-0500").
143         */
144        @SupportedVersions(V4_0)
145        public static final VCardDataType UTC_OFFSET = new VCardDataType("utc-offset");
146
147        /**
148         * A standardized abbreviation for a language (for example, "en-us" for
149         * American English).
150         * @see <a href="http://tools.ietf.org/html/rfc5646">RFC 5646</a>
151         */
152        @SupportedVersions(V4_0)
153        public static final VCardDataType LANGUAGE_TAG = new VCardDataType("language-tag");
154
155        private final String name;
156
157        /**
158         * @param name the data type name
159         */
160        private VCardDataType(String name) {
161                this.name = name;
162        }
163
164        /**
165         * Gets the name of the data type.
166         * @return the name of the data type (e.g. "text")
167         */
168        public String getName() {
169                return name;
170        }
171
172        /**
173         * <p>
174         * Gets the vCard versions that support this data type.
175         * </p>
176         * <p>
177         * The supported versions are defined by assigning a
178         * {@link SupportedVersions} annotation to the data type's static field (for
179         * example, {@link VCardDataType#CONTENT_ID}). Dynamically-created data
180         * types (i.e. non-standard data types) are considered to be supported by
181         * all versions.
182         * </p>
183         * @return the vCard versions that support this data type
184         */
185        public VCardVersion[] getSupportedVersions() {
186                for (Field field : getClass().getFields()) {
187                        if (!Modifier.isStatic(field.getModifiers())) {
188                                continue;
189                        }
190
191                        Object fieldValue;
192                        try {
193                                fieldValue = field.get(null);
194                        } catch (IllegalArgumentException e) {
195                                //should never be thrown because we check for the static modified
196                                continue;
197                        } catch (IllegalAccessException e) {
198                                continue;
199                        }
200
201                        if (fieldValue == this) {
202                                SupportedVersions supportedVersionsAnnotation = field.getAnnotation(SupportedVersions.class);
203                                return (supportedVersionsAnnotation == null) ? VCardVersion.values() : supportedVersionsAnnotation.value();
204                        }
205                }
206
207                return VCardVersion.values();
208        }
209
210        /**
211         * <p>
212         * Determines if this data type is supported by the given vCard version.
213         * </p>
214         * <p>
215         * The supported versions are defined by assigning a
216         * {@link SupportedVersions} annotation to the data type's static field (for
217         * example, {@link VCardDataType#CONTENT_ID}). Dynamically-created data
218         * types (i.e. non-standard data types) are considered to be supported by
219         * all versions.
220         * </p>
221         * @param version the vCard version
222         * @return true if it is supported, false if not
223         */
224        public boolean isSupportedBy(VCardVersion version) {
225                for (VCardVersion supportedVersion : getSupportedVersions()) {
226                        if (supportedVersion == version) {
227                                return true;
228                        }
229                }
230                return false;
231        }
232
233        @Override
234        public String toString() {
235                return name;
236        }
237
238        /**
239         * Searches for a data type that is defined as a static constant in this
240         * class.
241         * @param dataType the data type name (e.g. "text")
242         * @return the data type or null if not found
243         */
244        public static VCardDataType find(String dataType) {
245                return enums.find(dataType);
246        }
247
248        /**
249         * Searches for a data type and creates one if it cannot be found. All
250         * objects are guaranteed to be unique, so they can be compared with
251         * {@code ==} equality.
252         * @param dataType data type name (e.g. "text")
253         * @return the data type
254         */
255        public static VCardDataType get(String dataType) {
256                return enums.get(dataType);
257        }
258
259        /**
260         * Gets all of the data types that are defined as static constants in this
261         * class.
262         * @return the data types
263         */
264        public static Collection<VCardDataType> all() {
265                return enums.all();
266        }
267
268        /*
269         * Note: This class doesn't need equals() or hashCode() because the
270         * CaseClasses object enforces reference equality.
271         */
272
273        @Override
274        public int hashCode() {
275                return super.hashCode();
276        }
277
278        @Override
279        public boolean equals(Object obj) {
280                return super.equals(obj);
281        }
282}