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}