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}