001package ezvcard.io.scribe;
002
003import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueBuilder;
004import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueIterator;
005import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueBuilder;
006import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueIterator;
007
008import ezvcard.VCardDataType;
009import ezvcard.VCardVersion;
010import ezvcard.io.ParseContext;
011import ezvcard.io.html.HCardElement;
012import ezvcard.io.json.JCardValue;
013import ezvcard.io.text.WriteContext;
014import ezvcard.io.xml.XCardElement;
015import ezvcard.parameter.VCardParameters;
016import ezvcard.property.StructuredName;
017
018/*
019 Copyright (c) 2012-2026, 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
043/**
044 * Marshals {@link StructuredName} properties.
045 * @author Michael Angstadt
046 */
047public class StructuredNameScribe extends VCardPropertyScribe<StructuredName> {
048        public StructuredNameScribe() {
049                super(StructuredName.class, "N");
050        }
051
052        @Override
053        protected VCardDataType _defaultDataType(VCardVersion version) {
054                return VCardDataType.TEXT;
055        }
056
057        @Override
058        protected String _writeText(StructuredName property, WriteContext context) {
059                /*
060                 * StructuredValueBuilder cannot be used with 2.1 because it escapes
061                 * comma characters. For example, if someone's last name is "Foo,bar",
062                 * the comma character must NOT be escaped when written to a 2.1 vCard.
063                 * 
064                 * The reason commas are not escaped in 2.1 is because 2.1 does not
065                 * allow multi-valued components like 3.0 and 4.0 do (for example,
066                 * multiple suffixes).
067                 * 
068                 * If a StructuredName object has multi-valued components, and it is
069                 * being written to a 2.1 vCard, then ez-vcard will comma-delimit them
070                 * to prevent data loss. But this is not part of the 2.1 syntax.
071                 */
072                if (context.getVersion() == VCardVersion.V2_1) {
073                        SemiStructuredValueBuilder builder = new SemiStructuredValueBuilder();
074                        builder.append(property.getFamily());
075                        builder.append(property.getGiven());
076                        builder.append(String.join(",", property.getAdditionalNames()));
077                        builder.append(String.join(",", property.getPrefixes()));
078                        builder.append(String.join(",", property.getSuffixes()));
079                        return builder.build(false, context.isIncludeTrailingSemicolons());
080                } else {
081                        StructuredValueBuilder builder = new StructuredValueBuilder();
082                        builder.append(property.getFamily());
083                        builder.append(property.getGiven());
084                        builder.append(property.getAdditionalNames());
085                        builder.append(property.getPrefixes());
086                        builder.append(property.getSuffixes());
087                        return builder.build(context.isIncludeTrailingSemicolons());
088                }
089        }
090
091        @Override
092        protected StructuredName _parseText(String value, VCardDataType dataType, VCardParameters parameters, ParseContext context) {
093                StructuredName property = new StructuredName();
094
095                if (context.getVersion() == VCardVersion.V2_1) {
096                        /*
097                         * 2.1 does not recognize multi-valued components.
098                         */
099                        SemiStructuredValueIterator it = new SemiStructuredValueIterator(value);
100                        property.setFamily(it.next());
101                        property.setGiven(it.next());
102
103                        String next = it.next();
104                        if (next != null) {
105                                property.getAdditionalNames().add(next);
106                        }
107
108                        next = it.next();
109                        if (next != null) {
110                                property.getPrefixes().add(next);
111                        }
112
113                        next = it.next();
114                        if (next != null) {
115                                property.getSuffixes().add(next);
116                        }
117                } else {
118                        StructuredValueIterator it = new StructuredValueIterator(value);
119                        property.setFamily(it.nextValue());
120                        property.setGiven(it.nextValue());
121                        property.getAdditionalNames().addAll(it.nextComponent());
122                        property.getPrefixes().addAll(it.nextComponent());
123                        property.getSuffixes().addAll(it.nextComponent());
124                }
125
126                return property;
127        }
128
129        @Override
130        protected void _writeXml(StructuredName property, XCardElement parent) {
131                parent.append("surname", property.getFamily()); //the XML element still needs to be printed if value == null
132                parent.append("given", property.getGiven());
133                parent.append("additional", property.getAdditionalNames());
134                parent.append("prefix", property.getPrefixes());
135                parent.append("suffix", property.getSuffixes());
136        }
137
138        @Override
139        protected StructuredName _parseXml(XCardElement element, VCardParameters parameters, ParseContext context) {
140                StructuredName property = new StructuredName();
141
142                property.setFamily(s(element.first("surname")));
143                property.setGiven(s(element.first("given")));
144                property.getAdditionalNames().addAll(element.all("additional"));
145                property.getPrefixes().addAll(element.all("prefix"));
146                property.getSuffixes().addAll(element.all("suffix"));
147
148                return property;
149        }
150
151        private static String s(String value) {
152                return (value == null || value.isEmpty()) ? null : value;
153        }
154
155        @Override
156        protected StructuredName _parseHtml(HCardElement element, ParseContext context) {
157                StructuredName property = new StructuredName();
158
159                property.setFamily(s(element.firstValue("family-name")));
160                property.setGiven(s(element.firstValue("given-name")));
161                property.getAdditionalNames().addAll(element.allValues("additional-name"));
162                property.getPrefixes().addAll(element.allValues("honorific-prefix"));
163                property.getSuffixes().addAll(element.allValues("honorific-suffix"));
164
165                return property;
166        }
167
168        @Override
169        protected JCardValue _writeJson(StructuredName property) {
170                return JCardValue.structured(property.getFamily(), property.getGiven(), property.getAdditionalNames(), property.getPrefixes(), property.getSuffixes());
171        }
172
173        @Override
174        protected StructuredName _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, ParseContext context) {
175                StructuredName property = new StructuredName();
176                StructuredValueIterator it = new StructuredValueIterator(value.asStructured());
177
178                property.setFamily(it.nextValue());
179                property.setGiven(it.nextValue());
180                property.getAdditionalNames().addAll(it.nextComponent());
181                property.getPrefixes().addAll(it.nextComponent());
182                property.getSuffixes().addAll(it.nextComponent());
183
184                return property;
185        }
186}