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.Set; 009 010import ezvcard.VCard; 011import ezvcard.io.scribe.ScribeIndex; 012import ezvcard.io.scribe.VCardPropertyScribe; 013import ezvcard.parameter.AddressType; 014import ezvcard.property.Address; 015import ezvcard.property.Label; 016import ezvcard.property.VCardProperty; 017 018/* 019 Copyright (c) 2012-2023, 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 The views and conclusions contained in the software and documentation are those 043 of the authors and should not be interpreted as representing official policies, 044 either expressed or implied, of the FreeBSD Project. 045 */ 046 047/** 048 * Parses vCards from a data stream. 049 * @author Michael Angstadt 050 */ 051public abstract class StreamReader implements Closeable { 052 protected final List<ParseWarning> warnings = new ArrayList<>(); 053 protected ScribeIndex index = new ScribeIndex(); 054 protected ParseContext context; 055 056 /** 057 * Reads all vCards from the data stream. 058 * @return the vCards 059 * @throws IOException if there's a problem reading from the stream 060 */ 061 public List<VCard> readAll() throws IOException { 062 List<VCard> vcards = new ArrayList<>(); 063 VCard vcard; 064 while ((vcard = readNext()) != null) { 065 vcards.add(vcard); 066 } 067 return vcards; 068 } 069 070 /** 071 * Reads the next vCard from the data stream. 072 * @return the next vCard or null if there are no more 073 * @throws IOException if there's a problem reading from the stream 074 */ 075 public VCard readNext() throws IOException { 076 warnings.clear(); 077 context = new ParseContext(); 078 return _readNext(); 079 } 080 081 /** 082 * Reads the next vCard from the data stream. 083 * @return the next vCard or null if there are no more 084 * @throws IOException if there's a problem reading from the stream 085 */ 086 protected abstract VCard _readNext() throws IOException; 087 088 /** 089 * Matches up a list of {@link Label} properties with their corresponding 090 * {@link Address} properties. If no match can be found, then the LABEL 091 * property itself is assigned to the vCard. 092 * @param vcard the vCard that the properties belong to 093 * @param labels the LABEL properties 094 */ 095 protected void assignLabels(VCard vcard, List<Label> labels) { 096 List<Address> adrs = vcard.getAddresses(); 097 for (Label label : labels) { 098 boolean orphaned = true; 099 Set<AddressType> labelTypes = new HashSet<>(label.getTypes()); 100 for (Address adr : adrs) { 101 if (adr.getLabel() != null) { 102 //a label has already been assigned to it 103 continue; 104 } 105 106 Set<AddressType> adrTypes = new HashSet<>(adr.getTypes()); 107 if (adrTypes.equals(labelTypes)) { 108 adr.setLabel(label.getValue()); 109 orphaned = false; 110 break; 111 } 112 } 113 if (orphaned) { 114 vcard.addOrphanedLabel(label); 115 } 116 } 117 } 118 119 /** 120 * <p> 121 * Registers a property scribe. This is the same as calling: 122 * </p> 123 * <p> 124 * {@code getScribeIndex().register(scribe)} 125 * </p> 126 * @param scribe the scribe to register 127 */ 128 public void registerScribe(VCardPropertyScribe<? extends VCardProperty> scribe) { 129 index.register(scribe); 130 } 131 132 /** 133 * Gets the scribe index. 134 * @return the scribe index 135 */ 136 public ScribeIndex getScribeIndex() { 137 return index; 138 } 139 140 /** 141 * Sets the scribe index. 142 * @param index the scribe index 143 */ 144 public void setScribeIndex(ScribeIndex index) { 145 this.index = index; 146 } 147 148 /** 149 * Gets the warnings from the last vCard that was unmarshalled. This list is 150 * reset every time a new vCard is read. 151 * @return the warnings or empty list if there were no warnings 152 */ 153 public List<ParseWarning> getWarnings() { 154 return new ArrayList<>(warnings); 155 } 156}