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