001package ezvcard.io.scribe;
002
003import java.util.HashMap;
004import java.util.Map;
005
006import javax.xml.namespace.QName;
007
008import ezvcard.VCardVersion;
009import ezvcard.property.RawProperty;
010import ezvcard.property.VCardProperty;
011import ezvcard.property.Xml;
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
038/**
039 * <p>
040 * Manages a collection of property scribes (aka "marshallers" or "serializers")
041 * to use when reading or writing a vCard. The same instance of this object can
042 * be re-used across multiple vCard reader/writer objects. This is useful if you
043 * have custom scribe classes defined, as it allows you to only define them once
044 * instead of each time a vCard reader/writer object is created.
045 * </p>
046 * <p>
047 * <b>Example:</b>
048 * </p>
049 * 
050 * <pre class="brush:java">
051 * //init the index
052 * ScribeIndex index = new ScribeIndex();
053 * index.register(new CustomPropertyScribe());
054 * index.register(new AnotherCustomPropertyScribe());
055 * 
056 * //inject the ScribeIndex into a plain-text vCard reader class and read the vCard data stream
057 * try (VCardReader vcardReader = new VCardReader(...)) {
058 *   vcardReader.setScribeIndex(index);
059 *   List&lt;VCard&gt; vcards = new ArrayList&lt;VCard&gt;();
060 *   VCard vcard;
061 *   while ((vcards = vcardReader.readNext()) != null) {
062 *     vcards.add(vcard);
063 *   }
064 * }
065 * 
066 * //inject the same ScribeIndex instance into a jCard writer and write the vCards
067 * try (JCardWriter jcardWriter = new JCardWriter(...)) {
068 *   jcardWriter.setScribeIndex(index);
069 *   for (VCard vcard : vcards) {
070 *     jcardWriter.write(vcard);
071 *   }
072 * }
073 * </pre>
074 * @author Michael Angstadt
075 */
076public class ScribeIndex {
077        //define standard property scribes
078        private static final Map<String, VCardPropertyScribe<? extends VCardProperty>> standardByName = new HashMap<>();
079        private static final Map<Class<? extends VCardProperty>, VCardPropertyScribe<? extends VCardProperty>> standardByClass = new HashMap<>();
080        private static final Map<QName, VCardPropertyScribe<? extends VCardProperty>> standardByQName = new HashMap<>();
081        static {
082                //2.1, RFC 2426, RFC 6350
083                registerStandard(new AddressScribe());
084                registerStandard(new AgentScribe());
085                registerStandard(new AnniversaryScribe());
086                registerStandard(new BirthdayScribe());
087                registerStandard(new CalendarRequestUriScribe());
088                registerStandard(new CalendarUriScribe());
089                registerStandard(new CategoriesScribe());
090                registerStandard(new ClassificationScribe());
091                registerStandard(new ClientPidMapScribe());
092                registerStandard(new EmailScribe());
093                registerStandard(new FreeBusyUrlScribe());
094                registerStandard(new FormattedNameScribe());
095                registerStandard(new GenderScribe());
096                registerStandard(new GeoScribe());
097                registerStandard(new ImppScribe());
098                registerStandard(new KeyScribe());
099                registerStandard(new KindScribe());
100                registerStandard(new LabelScribe());
101                registerStandard(new LanguageScribe());
102                registerStandard(new LogoScribe());
103                registerStandard(new MailerScribe());
104                registerStandard(new MemberScribe());
105                registerStandard(new NicknameScribe());
106                registerStandard(new NoteScribe());
107                registerStandard(new OrganizationScribe());
108                registerStandard(new PhotoScribe());
109                registerStandard(new ProductIdScribe());
110                registerStandard(new ProfileScribe());
111                registerStandard(new RelatedScribe());
112                registerStandard(new RevisionScribe());
113                registerStandard(new RoleScribe());
114                registerStandard(new SortStringScribe());
115                registerStandard(new SoundScribe());
116                registerStandard(new SourceDisplayTextScribe());
117                registerStandard(new SourceScribe());
118                registerStandard(new StructuredNameScribe());
119                registerStandard(new TelephoneScribe());
120                registerStandard(new TimezoneScribe());
121                registerStandard(new TitleScribe());
122                registerStandard(new UidScribe());
123                registerStandard(new UrlScribe());
124
125                //RFC 6351
126                registerStandard(new XmlScribe());
127
128                //RFC 6474
129                registerStandard(new BirthplaceScribe());
130                registerStandard(new DeathdateScribe());
131                registerStandard(new DeathplaceScribe());
132
133                //RFC 6715
134                registerStandard(new ExpertiseScribe());
135                registerStandard(new OrgDirectoryScribe());
136                registerStandard(new InterestScribe());
137                registerStandard(new HobbyScribe());
138        }
139
140        private final Map<String, VCardPropertyScribe<? extends VCardProperty>> extendedByName = new HashMap<>(0);
141        private final Map<Class<? extends VCardProperty>, VCardPropertyScribe<? extends VCardProperty>> extendedByClass = new HashMap<>(0);
142        private final Map<QName, VCardPropertyScribe<? extends VCardProperty>> extendedByQName = new HashMap<>(0);
143
144        /**
145         * Gets a property scribe by name.
146         * @param propertyName the property name (case-insensitive, e.g. "FN")
147         * @return the property scribe or null if not found
148         */
149        public VCardPropertyScribe<? extends VCardProperty> getPropertyScribe(String propertyName) {
150                propertyName = propertyName.toUpperCase();
151
152                VCardPropertyScribe<? extends VCardProperty> scribe = extendedByName.get(propertyName);
153                if (scribe != null) {
154                        return scribe;
155                }
156
157                return standardByName.get(propertyName);
158        }
159
160        /**
161         * Determines if a scribe exists for a given property instance.
162         * @param property the property
163         * @return true if a scribe exists, false if not
164         */
165        public boolean hasPropertyScribe(VCardProperty property) {
166                if (property instanceof RawProperty) {
167                        return true;
168                }
169
170                return getPropertyScribe(property.getClass()) != null;
171        }
172
173        /**
174         * Gets a property scribe by class.
175         * @param clazz the property class
176         * @return the property scribe or null if not found
177         */
178        public VCardPropertyScribe<? extends VCardProperty> getPropertyScribe(Class<? extends VCardProperty> clazz) {
179                VCardPropertyScribe<? extends VCardProperty> scribe = extendedByClass.get(clazz);
180                if (scribe != null) {
181                        return scribe;
182                }
183
184                return standardByClass.get(clazz);
185        }
186
187        /**
188         * Gets the appropriate property scribe for a given property instance.
189         * @param property the property instance
190         * @return the property scribe or null if not found
191         */
192        public VCardPropertyScribe<? extends VCardProperty> getPropertyScribe(VCardProperty property) {
193                if (property instanceof RawProperty) {
194                        RawProperty raw = (RawProperty) property;
195                        return new RawPropertyScribe(raw.getPropertyName());
196                }
197
198                return getPropertyScribe(property.getClass());
199        }
200
201        /**
202         * Gets a property scribe by XML local name and namespace.
203         * @param qname the XML local name and namespace
204         * @return the property scribe or a {@link XmlScribe} if not found
205         */
206        public VCardPropertyScribe<? extends VCardProperty> getPropertyScribe(QName qname) {
207                VCardPropertyScribe<? extends VCardProperty> scribe = extendedByQName.get(qname);
208                if (scribe != null) {
209                        return scribe;
210                }
211
212                scribe = standardByQName.get(qname);
213                if (scribe != null) {
214                        return scribe;
215                }
216
217                if (VCardVersion.V4_0.getXmlNamespace().equals(qname.getNamespaceURI())) {
218                        return new RawPropertyScribe(qname.getLocalPart().toUpperCase());
219                }
220
221                return getPropertyScribe(Xml.class);
222        }
223
224        /**
225         * Registers a property scribe.
226         * @param scribe the scribe to register
227         */
228        public void register(VCardPropertyScribe<? extends VCardProperty> scribe) {
229                extendedByName.put(scribe.getPropertyName().toUpperCase(), scribe);
230                extendedByClass.put(scribe.getPropertyClass(), scribe);
231                extendedByQName.put(scribe.getQName(), scribe);
232        }
233
234        /**
235         * Unregisters a property scribe.
236         * @param scribe the scribe to unregister
237         */
238        public void unregister(VCardPropertyScribe<? extends VCardProperty> scribe) {
239                extendedByName.remove(scribe.getPropertyName().toUpperCase());
240                extendedByClass.remove(scribe.getPropertyClass());
241                extendedByQName.remove(scribe.getQName());
242        }
243
244        private static void registerStandard(VCardPropertyScribe<? extends VCardProperty> scribe) {
245                standardByName.put(scribe.getPropertyName().toUpperCase(), scribe);
246                standardByClass.put(scribe.getPropertyClass(), scribe);
247                standardByQName.put(scribe.getQName(), scribe);
248        }
249}