001/**
002 * The contents of this file are subject to the Mozilla Public License Version 1.1
003 * (the "License"); you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
005 * Software distributed under the License is distributed on an "AS IS" basis,
006 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
007 * specific language governing rights and limitations under the License.
008 *
009 * The Original Code is "ReadOnlyMessageIterator.java".  Description:
010 * "Iterator though existing Stuctures in a message.   "
011 *
012 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 * 2005.  All Rights Reserved.
014 *
015 * Contributor(s): ______________________________________.
016 *
017 * Alternatively, the contents of this file may be used under the terms of the
018 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
019 * applicable instead of those above.  If you wish to allow use of your version of this
020 * file only under the terms of the GPL and not to allow others to use your version
021 * of this file under the MPL, indicate your decision by deleting  the provisions above
022 * and replace  them with the notice and other provisions required by the GPL License.
023 * If you do not delete the provisions above, a recipient may use your version of
024 * this file under either the MPL or the GPL.
025 *
026 */
027package ca.uhn.hl7v2.util;
028
029import java.util.ArrayList;
030import java.util.Iterator;
031import java.util.List;
032import java.util.NoSuchElementException;
033
034import ca.uhn.hl7v2.HL7Exception;
035import ca.uhn.hl7v2.model.Group;
036import ca.uhn.hl7v2.model.Segment;
037import ca.uhn.hl7v2.model.Structure;
038
039/**
040 * Iterator though existing Stuctures in a message.  No new repetitions or optional 
041 * structures are created during iteration (in contrast to MessageIterator).  
042 * 
043 * Note that some structures are created during parsing, so the iteration may include 
044 * structures which were not present in the original encoded message.  If these are 
045 * not desired they can be skipped using a FilterIterator.  In fact to obtain an  
046 * iterator only over populated segments (not groups or empty segments) use the factory 
047 * method in this class.  
048 *  
049 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
050 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:27 $ by $Author: jamesagnew $
051 */
052public class ReadOnlyMessageIterator implements Iterator<Structure> {
053
054    private final List<Structure> myRemaining; //remaining nodes in reverse order (i.e. last is next)
055    
056    /**
057     * @param theRoot root of depth first iteration, which starts with the first child  
058     */
059    public ReadOnlyMessageIterator(Group theRoot) {
060        myRemaining = new ArrayList<>(40);
061        addChildren(theRoot);
062    }
063    
064    /**
065     * @param theRoot root of depth first iteration, which starts with the first child
066     * @return an iterator that skips groups and empty segments, returning only populated 
067     *      segments  
068     */
069    public static Iterator<Structure> createPopulatedSegmentIterator(Group theRoot) {
070        return createPopulatedStructureIterator(theRoot, Segment.class);       
071    }
072    
073    /**
074     * @param theRoot root of depth first iteration, which starts with the first child
075     * @param c structure class to look for
076     * @return an iterator that skips all structures that do not match the parameter
077     */
078    public static Iterator<Structure> createPopulatedStructureIterator(Group theRoot, Class<? extends Structure> c) {
079        return createPopulatedStructureIterator(theRoot, new StructurePredicate(c));       
080    }    
081
082    /**
083     * @param theRoot root of depth first iteration, which starts with the first child
084     * @param structureName structure name to look for
085     * @return an iterator that skips all structures that do not match the parameter
086     */
087    public static Iterator<Structure> createPopulatedStructureIterator(Group theRoot, String structureName) {
088        return createPopulatedStructureIterator(theRoot, new StructureNamePredicate(structureName));
089    }
090    
091    /**
092     * @param theRoot root of depth first iteration, which starts with the first child
093     * @param structureFilter filter class
094     * @return iterator that skips all structures that the filter does not accept
095     */
096    public static Iterator<Structure> createPopulatedStructureIterator(Group theRoot, FilterIterator.Predicate<Structure> structureFilter) {
097        Iterator<Structure> allIterator = new ReadOnlyMessageIterator(theRoot);
098        Iterator<Structure> structureIterator = new FilterIterator<>(allIterator, structureFilter);
099        
100        FilterIterator.Predicate<Structure> populatedOnly = obj -> {
101            try {
102                return !obj.isEmpty();
103            } catch (HL7Exception e) {
104                return false; // no exception expected
105            }
106        };
107        return new FilterIterator<>(structureIterator, populatedOnly);
108    }
109    
110    private void addChildren(Group theParent) {
111        String[] names = theParent.getNames();
112        for (int i = names.length - 1; i >= 0; i--) {
113            try {
114                Structure[] reps = theParent.getAll(names[i]);
115                for (int j = reps.length - 1; j >= 0; j--) {
116                    myRemaining.add(reps[j]);
117                }
118            } catch (HL7Exception e) {
119                throw new Error("Internal error: an invalid child name was obtained from its parent.");
120            }
121        }
122    }
123
124    /** 
125     * @see java.util.Iterator#hasNext()
126     */
127    public boolean hasNext() {
128        return !myRemaining.isEmpty();
129    }
130
131    /** 
132     * @see java.util.Iterator#next()
133     */
134    public Structure next() {
135        if (!hasNext()) {
136            throw new NoSuchElementException("No more nodes in message");
137        }
138        
139        Structure next = myRemaining.remove(myRemaining.size() - 1);
140        
141        if (next instanceof Group) {
142            addChildren((Group) next);
143        }
144        
145        return next;
146    }
147
148    /** 
149     * Not supported.  
150     */
151    public void remove() {
152        throw new UnsupportedOperationException("Can't remove a node from a message");
153    }
154
155}