001package ezvcard.io.scribe; 002 003import static ezvcard.util.StringUtils.join; 004 005import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueBuilder; 006import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueIterator; 007import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueBuilder; 008import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueIterator; 009 010import ezvcard.VCardDataType; 011import ezvcard.VCardVersion; 012import ezvcard.io.ParseContext; 013import ezvcard.io.html.HCardElement; 014import ezvcard.io.json.JCardValue; 015import ezvcard.io.text.WriteContext; 016import ezvcard.io.xml.XCardElement; 017import ezvcard.parameter.VCardParameters; 018import ezvcard.property.StructuredName; 019 020/* 021 Copyright (c) 2012-2023, Michael Angstadt 022 All rights reserved. 023 024 Redistribution and use in source and binary forms, with or without 025 modification, are permitted provided that the following conditions are met: 026 027 1. Redistributions of source code must retain the above copyright notice, this 028 list of conditions and the following disclaimer. 029 2. Redistributions in binary form must reproduce the above copyright notice, 030 this list of conditions and the following disclaimer in the documentation 031 and/or other materials provided with the distribution. 032 033 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 034 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 035 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 036 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 037 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 039 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 040 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 041 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 042 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 043 */ 044 045/** 046 * Marshals {@link StructuredName} properties. 047 * @author Michael Angstadt 048 */ 049public class StructuredNameScribe extends VCardPropertyScribe<StructuredName> { 050 public StructuredNameScribe() { 051 super(StructuredName.class, "N"); 052 } 053 054 @Override 055 protected VCardDataType _defaultDataType(VCardVersion version) { 056 return VCardDataType.TEXT; 057 } 058 059 @Override 060 protected String _writeText(StructuredName property, WriteContext context) { 061 /* 062 * StructuredValueBuilder cannot be used with 2.1 because it escapes 063 * comma characters. For example, if someone's last name is "Foo,bar", 064 * the comma character must NOT be escaped when written to a 2.1 vCard. 065 * 066 * The reason commas are not escaped in 2.1 is because 2.1 does not 067 * allow multi-valued components like 3.0 and 4.0 do (for example, 068 * multiple suffixes). 069 * 070 * If a StructuredName object has multi-valued components, and it is 071 * being written to a 2.1 vCard, then ez-vcard will comma-delimit them 072 * to prevent data loss. But this is not part of the 2.1 syntax. 073 */ 074 if (context.getVersion() == VCardVersion.V2_1) { 075 SemiStructuredValueBuilder builder = new SemiStructuredValueBuilder(); 076 builder.append(property.getFamily()); 077 builder.append(property.getGiven()); 078 builder.append(join(property.getAdditionalNames(), ",")); 079 builder.append(join(property.getPrefixes(), ",")); 080 builder.append(join(property.getSuffixes(), ",")); 081 return builder.build(false, context.isIncludeTrailingSemicolons()); 082 } else { 083 StructuredValueBuilder builder = new StructuredValueBuilder(); 084 builder.append(property.getFamily()); 085 builder.append(property.getGiven()); 086 builder.append(property.getAdditionalNames()); 087 builder.append(property.getPrefixes()); 088 builder.append(property.getSuffixes()); 089 return builder.build(context.isIncludeTrailingSemicolons()); 090 } 091 } 092 093 @Override 094 protected StructuredName _parseText(String value, VCardDataType dataType, VCardParameters parameters, ParseContext context) { 095 StructuredName property = new StructuredName(); 096 097 if (context.getVersion() == VCardVersion.V2_1) { 098 /* 099 * 2.1 does not recognize multi-valued components. 100 */ 101 SemiStructuredValueIterator it = new SemiStructuredValueIterator(value); 102 property.setFamily(it.next()); 103 property.setGiven(it.next()); 104 105 String next = it.next(); 106 if (next != null) { 107 property.getAdditionalNames().add(next); 108 } 109 110 next = it.next(); 111 if (next != null) { 112 property.getPrefixes().add(next); 113 } 114 115 next = it.next(); 116 if (next != null) { 117 property.getSuffixes().add(next); 118 } 119 } else { 120 StructuredValueIterator it = new StructuredValueIterator(value); 121 property.setFamily(it.nextValue()); 122 property.setGiven(it.nextValue()); 123 property.getAdditionalNames().addAll(it.nextComponent()); 124 property.getPrefixes().addAll(it.nextComponent()); 125 property.getSuffixes().addAll(it.nextComponent()); 126 } 127 128 return property; 129 } 130 131 @Override 132 protected void _writeXml(StructuredName property, XCardElement parent) { 133 parent.append("surname", property.getFamily()); //the XML element still needs to be printed if value == null 134 parent.append("given", property.getGiven()); 135 parent.append("additional", property.getAdditionalNames()); 136 parent.append("prefix", property.getPrefixes()); 137 parent.append("suffix", property.getSuffixes()); 138 } 139 140 @Override 141 protected StructuredName _parseXml(XCardElement element, VCardParameters parameters, ParseContext context) { 142 StructuredName property = new StructuredName(); 143 144 property.setFamily(s(element.first("surname"))); 145 property.setGiven(s(element.first("given"))); 146 property.getAdditionalNames().addAll(element.all("additional")); 147 property.getPrefixes().addAll(element.all("prefix")); 148 property.getSuffixes().addAll(element.all("suffix")); 149 150 return property; 151 } 152 153 private static String s(String value) { 154 return (value == null || value.isEmpty()) ? null : value; 155 } 156 157 @Override 158 protected StructuredName _parseHtml(HCardElement element, ParseContext context) { 159 StructuredName property = new StructuredName(); 160 161 property.setFamily(s(element.firstValue("family-name"))); 162 property.setGiven(s(element.firstValue("given-name"))); 163 property.getAdditionalNames().addAll(element.allValues("additional-name")); 164 property.getPrefixes().addAll(element.allValues("honorific-prefix")); 165 property.getSuffixes().addAll(element.allValues("honorific-suffix")); 166 167 return property; 168 } 169 170 @Override 171 protected JCardValue _writeJson(StructuredName property) { 172 return JCardValue.structured(property.getFamily(), property.getGiven(), property.getAdditionalNames(), property.getPrefixes(), property.getSuffixes()); 173 } 174 175 @Override 176 protected StructuredName _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, ParseContext context) { 177 StructuredName property = new StructuredName(); 178 StructuredValueIterator it = new StructuredValueIterator(value.asStructured()); 179 180 property.setFamily(it.nextValue()); 181 property.setGiven(it.nextValue()); 182 property.getAdditionalNames().addAll(it.nextComponent()); 183 property.getPrefixes().addAll(it.nextComponent()); 184 property.getSuffixes().addAll(it.nextComponent()); 185 186 return property; 187 } 188}