001    package ezvcard.types;
002    
003    import java.util.List;
004    
005    import javax.xml.namespace.QName;
006    
007    import org.w3c.dom.Element;
008    
009    import ezvcard.VCard;
010    import ezvcard.VCardSubTypes;
011    import ezvcard.VCardVersion;
012    import ezvcard.io.CompatibilityMode;
013    import ezvcard.io.EmbeddedVCardException;
014    import ezvcard.io.SkipMeException;
015    import ezvcard.util.HCardElement;
016    import ezvcard.util.XCardElement;
017    
018    /*
019     Copyright (c) 2012, Michael Angstadt
020     All rights reserved.
021    
022     Redistribution and use in source and binary forms, with or without
023     modification, are permitted provided that the following conditions are met: 
024    
025     1. Redistributions of source code must retain the above copyright notice, this
026     list of conditions and the following disclaimer. 
027     2. Redistributions in binary form must reproduce the above copyright notice,
028     this list of conditions and the following disclaimer in the documentation
029     and/or other materials provided with the distribution. 
030    
031     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
032     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
033     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
034     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
035     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
036     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
037     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
038     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
039     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
040     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
041    
042     The views and conclusions contained in the software and documentation are those
043     of the authors and should not be interpreted as representing official policies, 
044     either expressed or implied, of the FreeBSD Project.
045     */
046    
047    /**
048     * Represents a vCard key/value pair entry (called a "type" or "property").
049     * @author Michael Angstadt
050     */
051    public abstract class VCardType implements Comparable<VCardType> {
052            /**
053             * The name of the type.
054             */
055            protected final String typeName;
056    
057            /**
058             * The group that this type belongs to or null if it doesn't belong to a
059             * group.
060             */
061            protected String group;
062    
063            /**
064             * The list of attributes that are associated with this type (called
065             * "sub types" or "parameters").
066             */
067            protected VCardSubTypes subTypes = new VCardSubTypes();
068    
069            /**
070             * @param typeName the type name (e.g. "ADR")
071             */
072            public VCardType(String typeName) {
073                    this.typeName = typeName;
074            }
075    
076            /**
077             * Gets the name of this type.
078             * @return the type name (e.g. "ADR")
079             */
080            public String getTypeName() {
081                    return typeName;
082            }
083    
084            /**
085             * Gets the vCard versions that support this type.
086             * @return the vCard versions that support this type.
087             */
088            public VCardVersion[] getSupportedVersions() {
089                    return VCardVersion.values();
090            }
091    
092            /**
093             * Converts this type object to a string for sending over the wire. It is
094             * NOT responsible for folding.
095             * @param version the version vCard that is being generated
096             * @param warnings allows the programmer to alert the user to any
097             * note-worthy (but non-critical) issues that occurred during the
098             * marshalling process
099             * @param compatibilityMode allows the programmer to customize the
100             * marshalling process depending on the expected consumer of the vCard
101             * @return the string for sending over the wire
102             * @throws SkipMeException if this type should NOT be marshalled into the
103             * vCard
104             * @throws EmbeddedVCardException if the value of this type is an embedded
105             * vCard (i.e. the AGENT type)
106             */
107            public final String marshalText(VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
108                    StringBuilder sb = new StringBuilder();
109                    doMarshalText(sb, version, warnings, compatibilityMode);
110                    return sb.toString();
111            }
112    
113            /**
114             * Converts this type object to a string for sending over the wire. It is
115             * NOT responsible for folding.
116             * @param value the buffer to add the marshalled value to
117             * @param version the version vCard that is being generated
118             * @param warnings allows the programmer to alert the user to any
119             * note-worthy (but non-critical) issues that occurred during the
120             * marshalling process
121             * @param compatibilityMode allows the programmer to customize the
122             * marshalling process depending on the expected consumer of the vCard
123             * @throws SkipMeException if this type should NOT be marshalled into the
124             * vCard
125             * @throws EmbeddedVCardException if the value of this type is an embedded
126             * vCard (i.e. the AGENT type)
127             */
128            protected abstract void doMarshalText(StringBuilder value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode);
129    
130            /**
131             * Marshals this type for inclusion in an xCard (XML document).
132             * @param parent the XML element that the type's value will be inserted
133             * into. For example, this would be the "&lt;fn&gt;" element for the "FN"
134             * type.
135             * @param version the version vCard that is being generated
136             * @param warnings allows the programmer to alert the user to any
137             * note-worthy (but non-critical) issues that occurred during the
138             * marshalling process
139             * @param compatibilityMode allows the programmer to customize the
140             * marshalling process depending on the expected consumer of the vCard
141             * @throws SkipMeException if this type should NOT be marshalled into the
142             * vCard
143             */
144            public final void marshalXml(Element parent, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
145                    XCardElement wrapper = new XCardElement(parent, version);
146                    doMarshalXml(wrapper, warnings, compatibilityMode);
147            }
148    
149            /**
150             * Marshals this type for inclusion in an xCard (XML document). All child
151             * classes SHOULD override this, but are not required to.
152             * @param parent the XML element that the type's value will be inserted
153             * into. For example, this would be the "&lt;fn&gt;" element for the "FN"
154             * type.
155             * @param warnings allows the programmer to alert the user to any
156             * note-worthy (but non-critical) issues that occurred during the
157             * marshalling process
158             * @param compatibilityMode allows the programmer to customize the
159             * marshalling process depending on the expected consumer of the vCard
160             * @throws SkipMeException if this type should NOT be marshalled into the
161             * vCard
162             */
163            protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
164                    String value = marshalText(parent.version(), warnings, compatibilityMode);
165                    parent.append("unknown", value);
166            }
167    
168            /**
169             * Gets the Sub Types to send over the wire.
170             * @param version the version vCard that is being generated
171             * @param warnings allows the programmer to alert the user to any
172             * note-worthy (but non-critical) issues that occurred during the
173             * marshalling process
174             * @param compatibilityMode allows the programmer to customize the
175             * marshalling process depending on the expected consumer of the vCard
176             * @param vcard the vCard that is being marshalled
177             * @return the sub types that will be sent
178             */
179            public final VCardSubTypes marshalSubTypes(VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
180                    VCardSubTypes copy = new VCardSubTypes(subTypes);
181                    doMarshalSubTypes(copy, version, warnings, compatibilityMode, vcard);
182                    return copy;
183            }
184    
185            /**
186             * Gets the sub types that will be sent over the wire.
187             * 
188             * <p>
189             * If this method is NOT overridden, then the type's sub types will be sent
190             * over the wire as-is. In other words, whatever is in the
191             * {@link VCardType#subTypes} field will be sent. Child classes can override
192             * this method in order to modify the sub types before they are marshalled.
193             * </p>
194             * @param subTypes the sub types that will be marshalled into the vCard.
195             * This object is a copy of the {@link VCardType#subTypes} field, so any
196             * modifications done to this object will not effect the state of the field.
197             * @param version the version vCard that is being generated
198             * @param warnings allows the programmer to alert the user to any
199             * note-worthy (but non-critical) issues that occurred during the
200             * marshalling process
201             * @param compatibilityMode allows the programmer to customize the
202             * marshalling process depending on the expected consumer of the vCard
203             * @param vcard the {@link VCard} object that is being marshalled
204             */
205            protected void doMarshalSubTypes(VCardSubTypes subTypes, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
206                    //do nothing
207            }
208    
209            /**
210             * Unmarshals the type value from off the wire.
211             * @param subTypes the sub types that were parsed
212             * @param value the unfolded value from off the wire. If the wire value is
213             * in "quoted-printable" encoding, it will be decoded.
214             * @param version the version of the vCard that is being read or null if the
215             * VERSION type has not been parsed yet (v3.0 and v4.0 require that the
216             * VERSION type be at the top of the vCard, but v2.1 has no such
217             * requirement)
218             * @param warnings allows the programmer to alert the user to any
219             * note-worthy (but non-critical) issues that occurred during the
220             * unmarshalling process
221             * @param compatibilityMode allows the programmer to customize the
222             * unmarshalling process depending on where the vCard came from
223             * @throws SkipMeException if this type should NOT be added to the
224             * {@link VCard} object
225             * @throws EmbeddedVCardException if the value of this type is an embedded
226             * vCard (i.e. the AGENT type)
227             */
228            public final void unmarshalText(VCardSubTypes subTypes, String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
229                    this.subTypes = subTypes;
230                    doUnmarshalText(value, version, warnings, compatibilityMode);
231            }
232    
233            /**
234             * Unmarshals the type value from off the wire.
235             * @param value the unfolded value from off the wire. If the wire value is
236             * in the "quoted-printable" encoding, it will be decoded.
237             * @param version the version of the vCard that is being read or null if the
238             * VERSION type has not been parsed yet (v3.0 and v4.0 require that the
239             * VERSION type be at the top of the vCard, but v2.1 has no such
240             * requirement)
241             * @param warnings allows the programmer to alert the user to any
242             * note-worthy (but non-critical) issues that occurred during the
243             * unmarshalling process
244             * @param compatibilityMode allows you to customize the unmarshalling
245             * process depending on where the vCard came from
246             * @throws SkipMeException if this type should NOT be added to the
247             * {@link VCard} object
248             * @throws EmbeddedVCardException if the value of this type is an embedded
249             * vCard (i.e. the AGENT type)
250             */
251            protected abstract void doUnmarshalText(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode);
252    
253            /**
254             * Unmarshals the type from an xCard (XML document).
255             * @param subTypes the sub types that were parsed
256             * @param element the XML element that contains the type data. For example,
257             * this would be the "&lt;fn&gt;" element for the "FN" type. This object
258             * will NOT include the "&lt;parameters&gt;" child element (it is removed
259             * after being unmarshalled into a {@link VCardSubTypes} object).
260             * @param version the version of the xCard
261             * @param warnings allows the programmer to alert the user to any
262             * note-worthy (but non-critical) issues that occurred during the
263             * unmarshalling process
264             * @param compatibilityMode allows the programmer to customize the
265             * unmarshalling process depending on where the vCard came from
266             * @throws SkipMeException if this type should NOT be added to the
267             * {@link VCard} object
268             * @throws UnsupportedOperationException if the type class does not support
269             * xCard parsing
270             */
271            public final void unmarshalXml(VCardSubTypes subTypes, Element element, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
272                    this.subTypes = subTypes;
273                    XCardElement wrapper = new XCardElement(element, version);
274                    doUnmarshalXml(wrapper, warnings, compatibilityMode);
275            }
276    
277            /**
278             * Unmarshals the type from an xCard (XML document).
279             * @param element the XML element that contains the type data. For example,
280             * this would be the "&lt;fn&gt;" element for the "FN" type. This object
281             * will NOT include the "&lt;parameters&gt;" child element (it is removed
282             * after being unmarshalled into a {@link VCardSubTypes} object).
283             * @param warnings allows the programmer to alert the user to any
284             * note-worthy (but non-critical) issues that occurred during the
285             * unmarshalling process
286             * @param compatibilityMode allows the programmer to customize the
287             * unmarshalling process depending on where the vCard came from
288             * @throws SkipMeException if this type should NOT be added to the
289             * {@link VCard} object
290             * @throws UnsupportedOperationException if the type class does not support
291             * xCard parsing
292             */
293            protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
294                    throw new UnsupportedOperationException("This type class does not support the parsing of xCards.");
295            }
296    
297            /**
298             * Unmarshals the type from an hCard (HTML document).
299             * @param element the HTML element that contains the type data.
300             * @param warnings allows the programmer to alert the user to any
301             * note-worthy (but non-critical) issues that occurred during the
302             * unmarshalling process
303             * @throws SkipMeException if this type should NOT be added to the
304             * {@link VCard} object
305             * @throws EmbeddedVCardException if the value of this type is an embedded
306             * vCard (i.e. the AGENT type)
307             * @throws UnsupportedOperationException if the type class does not support
308             * hCard parsing
309             */
310            public final void unmarshalHtml(org.jsoup.nodes.Element element, List<String> warnings) {
311                    HCardElement hcardElement = new HCardElement(element);
312                    doUnmarshalHtml(hcardElement, warnings);
313            }
314    
315            /**
316             * Unmarshals the type from an hCard (HTML document).
317             * @param element the HTML element that contains the type data.
318             * @param warnings allows the programmer to alert the user to any
319             * note-worthy (but non-critical) issues that occurred during the
320             * unmarshalling process
321             * @throws SkipMeException if this type should NOT be added to the
322             * {@link VCard} object
323             * @throws EmbeddedVCardException if the value of this type is an embedded
324             * vCard (i.e. the AGENT type)
325             */
326            protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
327                    String value = element.value();
328                    doUnmarshalText(value, VCardVersion.V3_0, warnings, CompatibilityMode.RFC);
329            }
330    
331            /**
332             * <p>
333             * Gets the qualified name (XML namespace and local part) for marshalling
334             * the type to an XML document (xCard).
335             * </p>
336             * <p>
337             * Extended type classes should override this method. By default, this
338             * method returns <code>null</code>, which instructs the marshallers to
339             * assign the following qualified name to the type:<br>
340             * <br>
341             * Namespace: xCard namespace<br>
342             * Local part: a lower-cased version of the type name
343             * </p>
344             * @return the XML qualified name or null to use the default qualified name
345             */
346            public QName getQName() {
347                    return null;
348            }
349    
350            /**
351             * Gets all sub types (a.k.a "parameters") associated with this type. This
352             * method can be used to retrieve any extended, standard, or non-standard
353             * sub type.
354             * 
355             * <p>
356             * Ideally, this method should NOT be used to retrieve the values of
357             * standard sub types because the type class should contain getter/setter
358             * methods for each standard sub type. For example, instead of calling
359             * <code>NoteType.getSubTypes().getLanguage()</code> to retrieve the
360             * "LANGUAGE" sub type of a NOTE, the {@link NoteType#getLanguage()} method
361             * should be called instead.
362             * </p>
363             * @return the type's sub types
364             */
365            public VCardSubTypes getSubTypes() {
366                    return subTypes;
367            }
368    
369            /**
370             * Gets this type's group.
371             * @return the group or null if it does not belong to a group
372             */
373            public String getGroup() {
374                    return group;
375            }
376    
377            /**
378             * Sets this type's group.
379             * @param group the group or null to remove the type's group
380             */
381            public void setGroup(String group) {
382                    //TODO test for valid chars
383                    //              if (group != null && !group.matches("(?i)[-a-z0-9]+")) {
384                    //                      throw new IllegalArgumentException("");
385                    //              }
386                    this.group = group;
387            }
388    
389            /**
390             * Sorts by PREF parameter ascending. Types that do not have a PREF
391             * parameter are pushed to the end of the list.
392             */
393            public int compareTo(VCardType that) {
394                    Integer pref0 = this.getSubTypes().getPref();
395                    Integer pref1 = that.getSubTypes().getPref();
396                    if (pref0 == null && pref1 == null) {
397                            return 0;
398                    }
399                    if (pref0 == null) {
400                            return 1;
401                    }
402                    if (pref1 == null) {
403                            return -1;
404                    }
405                    return pref1.compareTo(pref0);
406            }
407    }