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;
038import ca.uhn.hl7v2.parser.EncodingCharacters;
039import ca.uhn.hl7v2.parser.PipeParser;
040
041/**
042 * Iterator though existing Stuctures in a message.  No new repetitions or optional 
043 * structures are created during iteration (in contrast to MessageIterator).  
044 * 
045 * Note that some structures are created during parsing, so the iteration may include 
046 * structures which were not present in the original encoded message.  If these are 
047 * not desired they can be skipped using a FilterIterator.  In fact to obtain an  
048 * iterator only over populated segments (not groups or empty segments) use the factory 
049 * method in this class.  
050 *  
051 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
052 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:27 $ by $Author: jamesagnew $
053 */
054public class ReadOnlyMessageIterator implements Iterator<Structure> {
055
056    private List<Structure> myRemaining; //remaining nodes in reverse order (i.e. last is next)
057    
058    /**
059     * @param theRoot root of depth first iteration, which starts with the first child  
060     */
061    public ReadOnlyMessageIterator(Group theRoot) {
062        myRemaining = new ArrayList<Structure>(40);
063        addChildren(theRoot);
064    }
065    
066    /**
067     * @param theRoot root of depth first iteration, which starts with the first child
068     * @return an iterator that skips groups and empty segments, returning only populated 
069     *      segments  
070     */
071    public static Iterator<Structure> createPopulatedSegmentIterator(Group theRoot) {
072        Iterator<Structure> allIterator = new ReadOnlyMessageIterator(theRoot);
073        
074        FilterIterator.Predicate<Structure> segmentsOnly = new FilterIterator.Predicate<Structure>() {
075            public boolean evaluate(Structure obj) {
076                if (Segment.class.isAssignableFrom(obj.getClass())) {
077                    return true;
078                } else {
079                    return false;
080                }
081            }
082        };
083        FilterIterator<Structure> segmentIterator = new FilterIterator<Structure>(allIterator, segmentsOnly);
084        
085        final EncodingCharacters ec = new EncodingCharacters('|', "^~\\&");
086        FilterIterator.Predicate<Structure> populatedOnly = new FilterIterator.Predicate<Structure>() {
087            public boolean evaluate(Structure obj) {
088                String encoded = PipeParser.encode((Segment) obj, ec);                
089                if (encoded.length() > 3) {
090                    return true;
091                } else {
092                    return false;
093                }
094            }
095        };
096        return new FilterIterator<Structure>(segmentIterator, populatedOnly);        
097    }
098    
099    private void addChildren(Group theParent) {
100        String[] names = theParent.getNames();
101        for (int i = names.length - 1; i >= 0; i--) {
102            try {
103                Structure[] reps = theParent.getAll(names[i]);
104                for (int j = reps.length - 1; j >= 0; j--) {
105                    myRemaining.add(reps[j]);
106                }
107            } catch (HL7Exception e) {
108                throw new Error("Internal error: an invalid child name was obtained from its parent.");
109            }
110        }
111    }
112
113    /** 
114     * @see java.util.Iterator#hasNext()
115     */
116    public boolean hasNext() {
117        return !myRemaining.isEmpty();
118    }
119
120    /** 
121     * @see java.util.Iterator#next()
122     */
123    public Structure next() {
124        if (!hasNext()) {
125            throw new NoSuchElementException("No more nodes in message");
126        }
127        
128        Structure next = myRemaining.remove(myRemaining.size() - 1);
129        
130        if (next instanceof Group) {
131            addChildren((Group) next);
132        }
133        
134        return next;
135    }
136
137    /** 
138     * Not supported.  
139     */
140    public void remove() {
141        throw new UnsupportedOperationException("Can't remove a node from a message");
142    }
143
144}