001package ezvcard.io; 002 003import java.io.Closeable; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Optional; 009import java.util.Set; 010 011import ezvcard.VCard; 012import ezvcard.io.scribe.ScribeIndex; 013import ezvcard.io.scribe.VCardPropertyScribe; 014import ezvcard.parameter.AddressType; 015import ezvcard.property.Address; 016import ezvcard.property.Label; 017import ezvcard.property.VCardProperty; 018 019/* 020 Copyright (c) 2012-2026, Michael Angstadt 021 All rights reserved. 022 023 Redistribution and use in source and binary forms, with or without 024 modification, are permitted provided that the following conditions are met: 025 026 1. Redistributions of source code must retain the above copyright notice, this 027 list of conditions and the following disclaimer. 028 2. Redistributions in binary form must reproduce the above copyright notice, 029 this list of conditions and the following disclaimer in the documentation 030 and/or other materials provided with the distribution. 031 032 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 033 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 034 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 035 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 036 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 037 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 038 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 039 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 040 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 041 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 042 043 The views and conclusions contained in the software and documentation are those 044 of the authors and should not be interpreted as representing official policies, 045 either expressed or implied, of the FreeBSD Project. 046 */ 047 048/** 049 * Parses vCards from a data stream. 050 * @author Michael Angstadt 051 */ 052public abstract class StreamReader implements Closeable { 053 protected final List<ParseWarning> warnings = new ArrayList<>(); 054 protected ScribeIndex index = new ScribeIndex(); 055 protected ParseContext context; 056 057 /** 058 * Reads all vCards from the data stream. 059 * @return the vCards 060 * @throws IOException if there's a problem reading from the stream 061 */ 062 public List<VCard> readAll() throws IOException { 063 List<VCard> vcards = new ArrayList<>(); 064 VCard vcard; 065 while ((vcard = readNext()) != null) { 066 vcards.add(vcard); 067 } 068 return vcards; 069 } 070 071 /** 072 * Reads the next vCard from the data stream. 073 * @return the next vCard or null if there are no more 074 * @throws IOException if there's a problem reading from the stream 075 */ 076 public VCard readNext() throws IOException { 077 warnings.clear(); 078 context = new ParseContext(); 079 return _readNext(); 080 } 081 082 /** 083 * Reads the next vCard from the data stream. 084 * @return the next vCard or null if there are no more 085 * @throws IOException if there's a problem reading from the stream 086 */ 087 protected abstract VCard _readNext() throws IOException; 088 089 /** 090 * Matches up a list of {@link Label} properties with their corresponding 091 * {@link Address} properties. If no match can be found, then the LABEL 092 * property itself is assigned to the vCard. 093 * @param vcard the vCard that the properties belong to 094 * @param labels the LABEL properties 095 */ 096 protected void assignLabels(VCard vcard, List<Label> labels) { 097 List<Address> adrs = vcard.getAddresses(); 098 for (Label label : labels) { 099 Set<AddressType> labelTypes = new HashSet<>(label.getTypes()); 100 101 //@formatter:off 102 Optional<Address> matchingAdr = adrs.stream() 103 .filter(adr -> adr.getLabel() == null) //only consider addresses that don't have a label assigned to them 104 .filter(adr -> labelTypes.equals(new HashSet<>(adr.getTypes()))) //do the types match? 105 .findFirst(); 106 //@formatter:on 107 108 if (matchingAdr.isPresent()) { 109 matchingAdr.get().setLabel(label.getValue()); 110 } else { 111 vcard.addOrphanedLabel(label); 112 } 113 } 114 } 115 116 /** 117 * <p> 118 * Registers a property scribe. This is the same as calling: 119 * </p> 120 * <p> 121 * {@code getScribeIndex().register(scribe)} 122 * </p> 123 * @param scribe the scribe to register 124 */ 125 public void registerScribe(VCardPropertyScribe<? extends VCardProperty> scribe) { 126 index.register(scribe); 127 } 128 129 /** 130 * Gets the scribe index. 131 * @return the scribe index 132 */ 133 public ScribeIndex getScribeIndex() { 134 return index; 135 } 136 137 /** 138 * Sets the scribe index. 139 * @param index the scribe index 140 */ 141 public void setScribeIndex(ScribeIndex index) { 142 this.index = index; 143 } 144 145 /** 146 * Gets the warnings from the last vCard that was unmarshalled. This list is 147 * reset every time a new vCard is read. 148 * @return the warnings or empty list if there were no warnings 149 */ 150 public List<ParseWarning> getWarnings() { 151 return new ArrayList<>(warnings); 152 } 153}