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 "AbstractMessage.java".  Description: 
010    "A default implementation of Message" 
011    
012    The Initial Developer of the Original Code is University Health Network. Copyright (C) 
013    2001.  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    */
027    
028    package ca.uhn.hl7v2.model;
029    
030    import java.io.IOException;
031    import java.util.GregorianCalendar;
032    import java.util.regex.Matcher;
033    import java.util.regex.Pattern;
034    
035    import ca.uhn.hl7v2.HL7Exception;
036    import ca.uhn.hl7v2.app.DefaultApplication;
037    import ca.uhn.hl7v2.model.primitive.CommonTS;
038    import ca.uhn.hl7v2.model.primitive.ID;
039    import ca.uhn.hl7v2.parser.ModelClassFactory;
040    import ca.uhn.hl7v2.parser.Parser;
041    import ca.uhn.hl7v2.parser.PipeParser;
042    import ca.uhn.hl7v2.util.MessageIDGenerator;
043    import ca.uhn.hl7v2.util.Terser;
044    import ca.uhn.hl7v2.validation.ValidationContext;
045    
046    /**
047     * A default implementation of Message. 
048     * @author Bryan Tripp (bryan_tripp@sourceforge.net)
049     */
050    @SuppressWarnings("serial")
051    public abstract class AbstractMessage extends AbstractGroup implements Message {
052        
053        private ValidationContext myContext;
054            private static final Pattern ourVersionPattern = Pattern.compile("\\.(v2[0-9][0-9]?)\\.");
055            private String myVersion;
056        private transient Parser myParser;
057            
058        /**
059         * @param theFactory factory for model classes (e.g. group, segment) for this message 
060         */
061        public AbstractMessage(ModelClassFactory theFactory) {
062            super(null, theFactory);
063        }
064        
065        /** 
066         * Returns this Message object.  
067         */
068        public Message getMessage() {
069           return this; 
070        }
071        
072            public Group getParent() {
073                    return this;
074            }
075    
076            /**
077         * Returns the version number.  This default implementation inspects 
078         * this.getClass().getName().  This should be overridden if you are putting
079         * a custom message definition in your own package, or it will default.  
080         * @see Message#getVersion()
081         * @returns 2.4 if not obvious from package name
082         */
083        public String getVersion() {
084            if (myVersion != null) {
085                    return myVersion;
086            }
087            
088            String version = null;
089            Pattern p = ourVersionPattern;
090            Matcher m = p.matcher(this.getClass().getName());
091            if (m.find()) {
092                String verFolder = m.group(1);
093                if (verFolder.length() > 0) {
094                    char[] chars = verFolder.toCharArray();
095                    StringBuffer buf = new StringBuffer();
096                    for (int i = 1; i < chars.length; i++) { //start at 1 to avoid the 'v'
097                        buf.append(chars[i]);
098                        if (i < chars.length - 1) buf.append('.');
099                    }
100                    version = buf.toString();
101                }
102            }
103            
104            if (version == null) 
105                version = "2.4";
106            
107            myVersion = version;
108            return version;
109        }
110        
111        /**
112         * @return the set of validation rules that applies to this message
113         */
114        public ValidationContext getValidationContext() {
115            return myContext;
116        }
117        
118        /**
119         * @param theContext the set of validation rules that are to apply to this message
120         */
121        public void setValidationContext(ValidationContext theContext) {
122            myContext = theContext;
123        }
124    
125        /**
126         * {@inheritDoc }
127         */
128        public Character getFieldSeparatorValue() throws HL7Exception {
129            Segment firstSegment = (Segment) get(getNames()[0]);
130            Primitive value = (Primitive) firstSegment.getField(1, 0);
131            String valueString = value.getValue();
132            if (valueString == null || valueString.length() == 0) {
133                return null;
134            }
135            return valueString.charAt(0);
136        }
137    
138    
139        /**
140         * {@inheritDoc }
141         */
142        public String getEncodingCharactersValue() throws HL7Exception {
143            Segment firstSegment = (Segment) get(getNames()[0]);
144            Primitive value = (Primitive) firstSegment.getField(2, 0);
145            return value.getValue();
146        }
147    
148    
149        /**
150         * <p>Sets the parser to be used when parse/encode methods are called on this
151         * Message, as well as its children. It is recommended that if these methods
152         * are going to be called, a parser be supplied with the validation context
153         * wanted. Where possible, the parser should be reused for best performance,
154         * unless thread safety is an issue.</p>
155         *
156         * <p>Note that not all parsers can be used. As of version 1.0, only {@link PipeParser}
157         * supports this functionality</p>
158         * 
159         * <p>Serialization note: The message parser is marked as transient, so it will not
160         * survive serialization.</p>
161         */
162        public void setParser(Parser parser) {
163            if (parser == null) {
164                throw new NullPointerException("Value may not be null");
165            }
166    
167            myParser = parser;
168        }
169    
170    
171        /**
172         * <p>Returns the parser to be used when parse/encode methods are called on this
173         * Message, as well as its children. The default value is a new {@link PipeParser}.</p>
174         * 
175         * <p>Serialization note: The message parser is marked as transient, so it will not
176         * survive serialization.</p>
177         */
178        public Parser getParser() {
179            if (myParser == null) {
180                myParser = new PipeParser();
181            }
182    
183            return myParser;
184        }
185    
186    
187        /**
188         * {@inheritDoc }
189         */
190        public void parse(String string) throws HL7Exception {
191            clear();
192                    getParser().parse(this, string);
193        }
194    
195        
196        /**
197         * {@inheritDoc }
198         */
199        public String encode() throws HL7Exception {
200            return getParser().encode(this);
201        }
202    
203        
204        /**
205         * {@inheritDoc }
206         */
207        public Message generateACK() throws HL7Exception, IOException {
208            return generateACK(null, null);
209        }
210    
211    
212        /**
213         * {@inheritDoc }
214         */
215        public Message generateACK(String theAcknowledgementCode, HL7Exception theException) throws HL7Exception, IOException {
216            Message retVal = DefaultApplication.makeACK(this);
217    
218            if (theAcknowledgementCode == null) {
219                theAcknowledgementCode = "AA";
220            }
221    
222            Segment msa = (Segment)retVal.get("MSA");
223            ID ackCode = (ID) msa.getField(1, 0);
224            ackCode.setValue(theAcknowledgementCode);
225    
226            if (theException != null) {
227                Segment err = (Segment) retVal.get("ERR");
228                theException.populate(err, null);
229            }
230    
231            return retVal;
232        }
233    
234        /**
235         * Provides an overview of the type and structure of this message
236         */
237        @Override
238        public String toString() {
239            try {
240                return encode();
241            } catch (HL7Exception e) {
242                return (getClass().getName() + " - Failed to create toString(): " + e.getMessage());
243            }
244        }
245    
246        /**
247         * {@inheritDoc}
248         */
249        public String printStructure() throws HL7Exception {
250            StringBuilder builder = new StringBuilder();    
251            appendStructureDescription(builder, 0, false, false, true, true);
252            return builder.toString();
253        }
254        
255        /**
256         * Quickly initializes this message with common values in the first (MSH) segment.
257         * 
258         * <p>
259         * Settings include:
260         *  <ul>
261         *          <li>MSH-1 (Field Separator) is set to "|"</li>
262         *          <li>MSH-2 (Encoding Characters) is set to "^~\&"</li>
263         *          <li>MSH-7 (Date/Time of Message) is set to current time</li>
264         *          <li>MSH-10 (Control ID) is populated using next value generated by {@link MessageIDGenerator}</li>
265         *  </ul>
266         * </p>
267         * 
268         * @param messageCode The message code (aka message type) to insert into MSH-9-1. Example: "ADT"
269         * @param messageTriggerEvent The message trigger event to insert into MSG-9-2. Example: "A01"
270         * @param processingId The message processing ID to insert into MSH-11. Examples: "T" (for TEST) or "P" for (PRODUCTION)
271         * 
272         * @throws IOException If the message ID generation fails for some reason 
273         * @throws HL7Exception If the message rejects any of the values which are generated to setting
274         */
275        public void initQuickstart(String messageCode, String messageTriggerEvent, String processingId) throws HL7Exception, IOException {
276            Segment msh = (Segment) get("MSH");
277            Terser.set(msh, 1, 0, 1, 1, "|");
278            Terser.set(msh, 2, 0, 1, 1, "^~\\&");
279            GregorianCalendar now = new GregorianCalendar();
280            Terser.set(msh, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now));
281            Terser.set(msh, 9, 0, 1, 1, messageCode);
282            Terser.set(msh, 9, 0, 2, 1, messageTriggerEvent);
283            Terser.set(msh, 10, 0, 1, 1, MessageIDGenerator.getInstance().getNewID());
284            Terser.set(msh, 11, 0, 1, 1, processingId);
285            Terser.set(msh, 12, 0, 1, 1, getVersion());
286            
287            if (getVersion().compareTo("2.4") >= 0) {
288                    String className = getClass().getName();
289                    int lastIndexOf = className.lastIndexOf('.');
290                            className = className.substring(lastIndexOf + 1);
291                            if (className.matches("[A-Z]{3}_[A-Z0-9]{3}")) {
292                            Terser.set(msh, 9, 0, 3, 1, className);
293                    }
294            }
295            
296        }
297        
298    }