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 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<Structure>(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<Structure>(allIterator, structureFilter);
099        
100        FilterIterator.Predicate<Structure> populatedOnly = new FilterIterator.Predicate<Structure>() {
101            public boolean evaluate(Structure obj) {
102                try {
103                    return !obj.isEmpty();
104                } catch (HL7Exception e) {
105                    return false; // no exception expected
106                }
107            }
108        };
109        return new FilterIterator<Structure>(structureIterator, populatedOnly);         
110    }
111    
112    private void addChildren(Group theParent) {
113        String[] names = theParent.getNames();
114        for (int i = names.length - 1; i >= 0; i--) {
115            try {
116                Structure[] reps = theParent.getAll(names[i]);
117                for (int j = reps.length - 1; j >= 0; j--) {
118                    myRemaining.add(reps[j]);
119                }
120            } catch (HL7Exception e) {
121                throw new Error("Internal error: an invalid child name was obtained from its parent.");
122            }
123        }
124    }
125
126    /** 
127     * @see java.util.Iterator#hasNext()
128     */
129    public boolean hasNext() {
130        return !myRemaining.isEmpty();
131    }
132
133    /** 
134     * @see java.util.Iterator#next()
135     */
136    public Structure next() {
137        if (!hasNext()) {
138            throw new NoSuchElementException("No more nodes in message");
139        }
140        
141        Structure next = myRemaining.remove(myRemaining.size() - 1);
142        
143        if (next instanceof Group) {
144            addChildren((Group) next);
145        }
146        
147        return next;
148    }
149
150    /** 
151     * Not supported.  
152     */
153    public void remove() {
154        throw new UnsupportedOperationException("Can't remove a node from a message");
155    }
156
157}