001/**
002The 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. 
004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
005Software distributed under the License is distributed on an "AS IS" basis, 
006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
007specific language governing rights and limitations under the License. 
008
009The Original Code is "DefaultValidator.java".  Description: 
010"A default conformance validator." 
011
012The Initial Developer of the Original Code is University Health Network. Copyright (C) 
0132003.  All Rights Reserved. 
014
015Contributor(s): ______________________________________. 
016
017Alternatively, the contents of this file may be used under the terms of the 
018GNU General Public License (the  �GPL�), in which case the provisions of the GPL are 
019applicable instead of those above.  If you wish to allow use of your version of this 
020file only under the terms of the GPL and not to allow others to use your version 
021of this file under the MPL, indicate your decision by deleting  the provisions above 
022and replace  them with the notice and other provisions required by the GPL License.  
023If you do not delete the provisions above, a recipient may use your version of 
024this file under either the MPL or the GPL. 
025
026 */
027
028package ca.uhn.hl7v2.conf.check;
029
030import java.io.BufferedReader;
031import java.io.File;
032import java.io.FileReader;
033import java.io.IOException;
034import java.util.ArrayList;
035import java.util.List;
036
037import ca.uhn.hl7v2.model.*;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import ca.uhn.hl7v2.DefaultHapiContext;
042import ca.uhn.hl7v2.HL7Exception;
043import ca.uhn.hl7v2.HapiContext;
044import ca.uhn.hl7v2.HapiContextSupport;
045import ca.uhn.hl7v2.conf.ProfileException;
046import ca.uhn.hl7v2.conf.parser.ProfileParser;
047import ca.uhn.hl7v2.conf.spec.RuntimeProfile;
048import ca.uhn.hl7v2.conf.spec.message.AbstractComponent;
049import ca.uhn.hl7v2.conf.spec.message.AbstractSegmentContainer;
050import ca.uhn.hl7v2.conf.spec.message.Component;
051import ca.uhn.hl7v2.conf.spec.message.Field;
052import ca.uhn.hl7v2.conf.spec.message.ProfileStructure;
053import ca.uhn.hl7v2.conf.spec.message.Seg;
054import ca.uhn.hl7v2.conf.spec.message.SegGroup;
055import ca.uhn.hl7v2.conf.spec.message.StaticDef;
056import ca.uhn.hl7v2.conf.spec.message.SubComponent;
057import ca.uhn.hl7v2.conf.store.CodeStore;
058import ca.uhn.hl7v2.conf.store.ProfileStoreFactory;
059import ca.uhn.hl7v2.parser.EncodingCharacters;
060import ca.uhn.hl7v2.parser.GenericParser;
061import ca.uhn.hl7v2.parser.Parser;
062import ca.uhn.hl7v2.parser.PipeParser;
063import ca.uhn.hl7v2.util.Terser;
064
065/**
066 * A default conformance profile validator.
067 * 
068 * Note: this class is currently NOT thread-safe!
069 * 
070 * @author Bryan Tripp
071 */
072public class DefaultValidator extends HapiContextSupport implements Validator {
073
074        private EncodingCharacters enc; // used to check for content in parts of a message
075        private static final Logger log = LoggerFactory.getLogger(DefaultValidator.class);
076        private boolean validateChildren = true;
077        private CodeStore codeStore;
078
079        /** Creates a new instance of DefaultValidator */
080        public DefaultValidator() {
081            this(new DefaultHapiContext());
082        }
083        
084    public DefaultValidator(HapiContext context) {
085        super(context);
086        enc = new EncodingCharacters('|', null); // the | is assumed later -- don't change
087    }   
088
089        /**
090         * If set to false (default is true), each testXX and validateXX method will only test the
091         * direct object it is responsible for, not its children.
092         */
093        public void setValidateChildren(boolean validateChildren) {
094                this.validateChildren = validateChildren;
095        }
096
097        /**
098         * <p>
099         * Provides a code store to use to provide the code tables which will be used to validate coded
100         * value types. If a code store has not been set (which is the default),
101         * {@link ProfileStoreFactory} will be checked for an appropriate code store, and if none is
102         * found then coded values will not be validated.
103         * </p>
104         */
105        public void setCodeStore(CodeStore theCodeStore) {
106                codeStore = theCodeStore;
107        }
108
109        /**
110         * @see Validator#validate
111         */
112        public HL7Exception[] validate(Message message, StaticDef profile) throws ProfileException,
113                        HL7Exception {
114                List<HL7Exception> exList = new ArrayList<HL7Exception>();
115                Terser t = new Terser(message);
116
117        checkMessageType(t.get("/MSH-9-1"), profile, exList);
118        checkEventType(t.get("/MSH-9-2"), profile, exList);
119        checkMessageStructure(t.get("/MSH-9-3"), profile, exList);
120
121        exList.addAll(doTestGroup(message, profile, profile.getIdentifier(),
122                                validateChildren));
123                return exList.toArray(new HL7Exception[exList.size()]);
124        }
125
126
127    protected void checkEventType(String evType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception {
128        if (!evType.equals(profile.getEventType())
129                && !profile.getEventType().equalsIgnoreCase("ALL")) {
130            HL7Exception e = new ProfileNotFollowedException("Event type " + evType
131                    + " doesn't match profile type of " + profile.getEventType());
132            exList.add(e);
133        }
134    }
135
136    protected void checkMessageType(String msgType, StaticDef profile, List<HL7Exception> exList) throws HL7Exception {
137        if (!msgType.equals(profile.getMsgType())) {
138            HL7Exception e = new ProfileNotFollowedException("Message type " + msgType
139                    + " doesn't match profile type of " + profile.getMsgType());
140            exList.add(e);
141        }
142    }
143
144    protected void checkMessageStructure(String msgStruct, StaticDef profile, List<HL7Exception> exList) {
145        if (msgStruct == null || !msgStruct.equals(profile.getMsgStructID())) {
146            HL7Exception e = new ProfileNotFollowedException("Message structure " + msgStruct
147                    + " doesn't match profile type of " + profile.getMsgStructID());
148            exList.add(e);
149        }
150    }
151
152        /**
153         * Tests a group against a group section of a profile.
154         */
155        public List<HL7Exception> testGroup(Group group, SegGroup profile, String profileID)
156                        throws ProfileException {
157                return doTestGroup(group, profile, profileID, true);
158        }
159
160        protected List<HL7Exception> doTestGroup(Group group, AbstractSegmentContainer profile,
161                        String profileID, boolean theValidateChildren) throws ProfileException {
162                List<HL7Exception> exList = new ArrayList<HL7Exception>();
163                List<String> allowedStructures = new ArrayList<String>();
164
165                for (ProfileStructure struct : profile) {
166
167                        // only test a structure in detail if it isn't X
168                        if (!struct.getUsage().equalsIgnoreCase("X")) {
169                                allowedStructures.add(struct.getName());
170
171                                // see which instances have content
172                                try {
173                                        List<Structure> instancesWithContent = new ArrayList<Structure>();
174                                        for (Structure instance : group.getAll(struct.getName())) {
175                                                if (!instance.isEmpty())
176                                                        instancesWithContent.add(instance);
177                                        }
178
179                                        testCardinality(instancesWithContent.size(), struct.getMin(),
180                                                        struct.getMax(), struct.getUsage(), struct.getName(), exList);
181
182                                        // test children on instances with content
183                                        if (theValidateChildren) {
184                                                for (Structure s : instancesWithContent) {
185                            exList.addAll(testStructure(s, struct, profileID));
186                                                }
187                                        }
188
189                                } catch (HL7Exception he) {
190                                        exList.add(new ProfileNotHL7CompliantException(struct.getName()
191                                                        + " not found in message"));
192                                }
193                        }
194                }
195
196                // complain about X structures that have content
197       checkForExtraStructures(group, allowedStructures, exList);
198
199                return exList;
200        }
201
202        /**
203         * Checks a group's children against a list of allowed structures for the group (ie those
204         * mentioned in the profile with usage other than X). Returns a list of exceptions representing
205         * structures that appear in the message but are not supposed to.
206         */
207        protected void checkForExtraStructures(Group group, List<String> allowedStructures, List<HL7Exception> exList)
208                        throws ProfileException {
209                for (String childName : group.getNames()) {
210                        if (!allowedStructures.contains(childName)) {
211                                try {
212                                        for (Structure rep : group.getAll(childName)) {
213                                                if (!rep.isEmpty()) {
214                                                        HL7Exception e = new XElementPresentException("The structure "
215                                                                        + childName + " appears in the message but not in the profile");
216                                                        exList.add(e);
217                                                }
218                                        }
219                                } catch (HL7Exception he) {
220                                        throw new ProfileException("Problem checking profile", he);
221                                }
222                        }
223                }
224        }
225
226        /**
227         * Checks cardinality and creates an appropriate exception if out of bounds. The usage code is
228         * needed because if min cardinality is > 0, the min # of reps is only required if the usage
229         * code is 'R' (see HL7 v2.5 section 2.12.6.4).
230         * 
231         * @param reps the number of reps
232         * @param min the minimum number of reps
233         * @param max the maximum number of reps (-1 means *)
234         * @param usage the usage code
235         * @param name the name of the repeating structure (used in exception msg)
236         * @return null if cardinality OK, exception otherwise
237         */
238        protected HL7Exception testCardinality(int reps, int min, int max, String usage, String name, List<HL7Exception> exList) {
239                HL7Exception e = null;
240                if (reps < min && usage.equalsIgnoreCase("R")) {
241            e = new ProfileNotFollowedException(name + " must have at least " + min
242                                        + " repetitions (has " + reps + ")");
243                } else if (max > 0 && reps > max) {
244            e = new ProfileNotFollowedException(name + " must have no more than " + max
245                                        + " repetitions (has " + reps + ")");
246                }
247        if (e != null) exList.add(e);
248        return e;
249        }
250
251        /**
252         * Tests a structure (segment or group) against the corresponding part of a profile.
253         */
254        public List<HL7Exception> testStructure(Structure s, ProfileStructure profile, String profileID)
255                        throws ProfileException {
256                List<HL7Exception> exList = new ArrayList<HL7Exception>();
257                if (profile instanceof Seg) {
258                        if (Segment.class.isAssignableFrom(s.getClass())) {
259                                exList.addAll(doTestSegment((Segment) s, (Seg) profile, profileID, validateChildren));
260                        } else {
261                                exList.add(new ProfileNotHL7CompliantException(
262                                                "Mismatch between a segment in the profile and the structure "
263                                                                + s.getClass().getName() + " in the message"));
264                        }
265                } else if (profile instanceof SegGroup) {
266                        if (Group.class.isAssignableFrom(s.getClass())) {
267                exList.addAll(testGroup((Group) s, (SegGroup) profile, profileID));
268                        } else {
269                                exList.add(new ProfileNotHL7CompliantException(
270                                                "Mismatch between a group in the profile and the structure "
271                                                                + s.getClass().getName() + " in the message"));
272                        }
273                }
274                return exList;
275        }
276
277        /**
278         * Tests a segment against a segment section of a profile.
279         */
280        public List<HL7Exception> testSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile,
281                        String profileID) throws ProfileException {
282                return doTestSegment(segment, profile, profileID, true);
283        }
284
285        protected List<HL7Exception> doTestSegment(ca.uhn.hl7v2.model.Segment segment, Seg profile,
286                        String profileID, boolean theValidateChildren) throws ProfileException {
287                List<HL7Exception> exList = new ArrayList<HL7Exception>();
288                List<Integer> allowedFields = new ArrayList<Integer>();
289
290                for (int i = 1; i <= profile.getFields(); i++) {
291                        Field field = profile.getField(i);
292
293                        // only test a field in detail if it isn't X
294                        if (!field.getUsage().equalsIgnoreCase("X")) {
295                                allowedFields.add(i);
296
297                                // see which instances have content
298                                try {
299                                        Type[] instances = segment.getField(i);
300                                        List<Type> instancesWithContent = new ArrayList<Type>();
301                                        for (Type instance : instances) {
302                                                if (!instance.isEmpty())
303                                                        instancesWithContent.add(instance);
304                                        }
305
306                                        HL7Exception ce = testCardinality(instancesWithContent.size(), field.getMin(),
307                                                        field.getMax(), field.getUsage(), field.getName(), exList);
308                                        if (ce != null) {
309                                                ce.setFieldPosition(i);
310                                        }
311
312                                        // test field instances with content
313                                        if (theValidateChildren) {
314                                                for (Type s : instancesWithContent) {
315                                                        // escape field value when checking length
316                            boolean escape = !(profile.getName().equalsIgnoreCase("MSH") && i < 3);
317                            List<HL7Exception> childExceptions = doTestField(s, field, escape,
318                                                                        profileID, validateChildren);
319                                                        for (HL7Exception ex : childExceptions) {
320                                ex.setFieldPosition(i);
321                                                        }
322                            exList.addAll(childExceptions);
323                                                }
324                                        }
325
326                                } catch (HL7Exception he) {
327                                        exList.add(new ProfileNotHL7CompliantException("Field " + i
328                                                        + " not found in message"));
329                                }
330                        }
331
332                }
333
334                // complain about X fields with content
335                checkForExtraFields(segment, allowedFields, exList);
336
337                for (HL7Exception ex : exList) {
338            ex.setSegmentName(profile.getName());
339                }
340                return exList;
341        }
342
343        /**
344         * Checks a segment against a list of allowed fields (ie those mentioned in the profile with
345         * usage other than X). Returns a list of exceptions representing field that appear but are not
346         * supposed to.
347         * 
348         * @param allowedFields an array of Integers containing field #s of allowed fields
349         */
350        protected void checkForExtraFields(Segment segment, List<Integer> allowedFields, List<HL7Exception> exList)
351                        throws ProfileException {
352                for (int i = 1; i <= segment.numFields(); i++) {
353                        if (!allowedFields.contains(i)) {
354                                try {
355                                        Type[] reps = segment.getField(i);
356                                        for (Type rep : reps) {
357                                                if (!rep.isEmpty()) {
358                                                        HL7Exception e = new XElementPresentException("Field " + i + " in "
359                                                                        + segment.getName()
360                                                                        + " appears in the message but not in the profile");
361                                                        exList.add(e);
362                                                }
363                                        }
364                                } catch (HL7Exception he) {
365                                        throw new ProfileException("Problem testing against profile", he);
366                                }
367                        }
368                }
369        }
370
371        /**
372         * Tests a Type against the corresponding section of a profile.
373         * 
374         * @param encoded optional encoded form of type (if you want to specify this -- if null, default
375         *            pipe-encoded form is used to check length and constant val)
376         */
377        public List<HL7Exception> testType(Type type, AbstractComponent<?> profile, String encoded,
378                        String profileID) {
379                List<HL7Exception> exList = new ArrayList<HL7Exception>();
380                if (encoded == null)
381                        encoded = PipeParser.encode(type, this.enc);
382
383                testUsage(encoded, profile.getUsage(), profile.getName(), exList);
384
385                if (!profile.getUsage().equals("X")) {
386            checkDataType(profile.getDatatype(), type, exList);
387            checkLength(profile.getLength(), profile.getName(), encoded, exList);
388            checkConstantValue(profile.getConstantValue(), encoded, exList);
389
390            testTypeAgainstTable(type, profile, profileID, exList);
391                }
392
393                return exList;
394        }
395
396    protected void checkConstantValue(String value, String encoded, List<HL7Exception> exList) {
397        // check constant value
398        if (value != null && value.length() > 0) {
399            if (!encoded.equals(value))
400                exList.add(new ProfileNotFollowedException("'" + encoded
401                        + "' doesn't equal constant value of '" + value + "'"));
402        }
403    }
404
405    protected void checkLength(long length, String name, String encoded, List<HL7Exception> exList) {
406        // check length
407        if (encoded.length() > length)
408            exList.add(new ProfileNotFollowedException("The type " + name
409                    + " has length " + encoded.length() + " which exceeds max of "
410                    + length));
411    }
412
413    protected void checkDataType(String dataType, Type type, List<HL7Exception> exList) {
414        // check datatype
415        String typeName = type.getName();
416        if (!(type instanceof Varies || typeName.equals(dataType))) {
417            exList.add(new ProfileNotHL7CompliantException("HL7 datatype " + typeName
418                    + " doesn't match profile datatype " + dataType));
419        }
420    }
421
422    /**
423         * Tests whether the given type falls within a maximum length.
424         * 
425         * @return null of OK, an HL7Exception otherwise
426         */
427        public HL7Exception testLength(Type type, int maxLength) {
428                HL7Exception e = null;
429                String encoded = PipeParser.encode(type, this.enc);
430                if (encoded.length() > maxLength) {
431                        e = new ProfileNotFollowedException("Length of " + encoded.length()
432                                        + " exceeds maximum of " + maxLength);
433                }
434                return e;
435        }
436
437        /**
438         * Tests an element against the corresponding usage code. The element is required in its encoded
439         * form.
440         * 
441         * @param encoded the pipe-encoded message element
442         * @param usage the usage code (e.g. "CE")
443         * @param name the name of the element (for use in exception messages)
444         * @return null if there is no problem, an HL7Exception otherwise
445         */
446        protected void testUsage(String encoded, String usage, String name, List<HL7Exception> exList) {
447                if (usage.equalsIgnoreCase("R")) {
448                        if (encoded.length() == 0)
449                                exList.add(new ProfileNotFollowedException("Required element " + name + " is missing"));
450                } else if (usage.equalsIgnoreCase("RE")) {
451                        // can't test anything
452                } else if (usage.equalsIgnoreCase("O")) {
453                        // can't test anything
454                } else if (usage.equalsIgnoreCase("C")) {
455                        // can't test anything yet -- wait for condition syntax in v2.6
456                } else if (usage.equalsIgnoreCase("CE")) {
457                        // can't test anything
458                } else if (usage.equalsIgnoreCase("X")) {
459                        if (encoded.length() > 0)
460                                exList.add(new XElementPresentException("Element \"" + name
461                                                + "\" is present but specified as not used (X)"));
462                } else if (usage.equalsIgnoreCase("B")) {
463                        // can't test anything
464                }
465        }
466
467        /**
468         * Tests table values for ID, IS, and CE types. An empty list is returned for all other types or
469         * if the table name or number is missing.
470         */
471    protected void testTypeAgainstTable(Type type, AbstractComponent<?> profile,
472                        String profileID, List<HL7Exception> exList) {
473                if (profile.getTable() != null
474                                && (type.getName().equals("IS") || type.getName().equals("ID"))) {
475                        String codeSystem = String.format("HL7%1$4s", profile.getTable()).replace(" ", "0");
476                        String value = ((Primitive) type).getValue();
477                        addTableTestResult(profileID, codeSystem, value, exList);
478                } else if (type.getName().equals("CE")) {
479                        String value = Terser.getPrimitive(type, 1, 1).getValue();
480                        String codeSystem = Terser.getPrimitive(type, 3, 1).getValue();
481                        addTableTestResult(profileID, codeSystem, value, exList);
482
483                        value = Terser.getPrimitive(type, 4, 1).getValue();
484                        codeSystem = Terser.getPrimitive(type, 6, 1).getValue();
485                        addTableTestResult(profileID, codeSystem, value, exList);
486                }
487        }
488
489        protected void addTableTestResult(String profileID, String codeSystem, String value, List<HL7Exception> exList) {
490                if (codeSystem != null && value != null && validateChildren) {
491                        testValueAgainstTable(profileID, codeSystem, value, exList);
492                }
493        }
494
495        protected void testValueAgainstTable(String profileID, String codeSystem, String value, List<HL7Exception> exList) {
496                CodeStore store = codeStore;
497                if (codeStore == null) {
498                        store = getHapiContext().getCodeStoreRegistry().getCodeStore(profileID, codeSystem);
499                }
500
501                if (store == null) {
502                        log.info(
503                                        "Not checking value {}: no code store was found for profile {} code system {}",
504                                        new Object[] { value, profileID, codeSystem });
505                } else {
506                        if (!store.knowsCodes(codeSystem)) {
507                                log.warn("Not checking value {}: Don't have a table for code system {}", value,
508                                                codeSystem);
509                        } else if (!store.isValidCode(codeSystem, value)) {
510                                exList.add(new ProfileNotFollowedException("Code '" + value + "' not found in table "
511                                                + codeSystem + ", profile " + profileID));
512                        }
513                }
514
515        }
516
517        public List<HL7Exception> testField(Type type, Field profile, boolean escape, String profileID)
518                        throws ProfileException, HL7Exception {
519                return doTestField(type, profile, escape, profileID, true);
520        }
521
522        protected List<HL7Exception> doTestField(Type type, Field profile, boolean escape, String profileID,
523                        boolean theValidateChildren) throws ProfileException, HL7Exception {
524                List<HL7Exception> exList = new ArrayList<HL7Exception>();
525
526                // account for MSH 1 & 2 which aren't escaped
527                String encoded = null;
528                if (!escape && Primitive.class.isAssignableFrom(type.getClass()))
529                        encoded = ((Primitive) type).getValue();
530
531                exList.addAll(testType(type, profile, encoded, profileID));
532
533                // test children
534                if (theValidateChildren) {
535                        if (profile.getComponents() > 0 && !profile.getUsage().equals("X")) {
536                                if (Composite.class.isAssignableFrom(type.getClass())) {
537                                        Composite comp = (Composite) type;
538                                        for (int i = 1; i <= profile.getComponents(); i++) {
539                                                Component childProfile = profile.getComponent(i);
540                                                try {
541                                                        Type child = comp.getComponent(i - 1);
542                                                        exList.addAll(doTestComponent(child, childProfile, profileID, validateChildren));
543                                                } catch (DataTypeException de) {
544                                                        exList.add(new ProfileNotHL7CompliantException(
545                                                                        "More components in profile than allowed in message: "
546                                                                                        + de.getMessage()));
547                                                }
548                                        }
549                                        checkExtraComponents(comp, profile.getComponents(), exList);
550                                } else {
551                                        exList.add(new ProfileNotHL7CompliantException("A field has type primitive "
552                                                        + type.getClass().getName() + " but the profile defines components"));
553                                }
554                        }
555                }
556
557                return exList;
558        }
559
560        public List<HL7Exception> testComponent(Type type, Component profile, String profileID)
561                        throws ProfileException, HL7Exception {
562                return doTestComponent(type, profile, profileID, true);
563        }
564
565        protected List<HL7Exception> doTestComponent(Type type, Component profile, String profileID,
566                        boolean theValidateChildren) throws ProfileException, HL7Exception {
567                List<HL7Exception> exList = new ArrayList<HL7Exception>();
568                exList.addAll(testType(type, profile, null, profileID));
569
570                // test children
571                if (profile.getSubComponents() > 0 && !profile.getUsage().equals("X") && (!type.isEmpty())) {
572                        if (Composite.class.isAssignableFrom(type.getClass())) {
573                                Composite comp = (Composite) type;
574
575                                if (theValidateChildren) {
576                                        for (int i = 1; i <= profile.getSubComponents(); i++) {
577                                                SubComponent childProfile = profile.getSubComponent(i);
578                                                try {
579                                                        Type child = comp.getComponent(i - 1);
580                                                        exList.addAll(testType(child, childProfile, null, profileID));
581                                                } catch (DataTypeException de) {
582                                                        exList.add(new ProfileNotHL7CompliantException(
583                                                                        "More subcomponents in profile than allowed in message: "
584                                                                                        + de.getMessage()));
585                                                }
586                                        }
587                                }
588
589                checkExtraComponents(comp, profile.getSubComponents(), exList);
590                        } else {
591                                exList.add(new ProfileNotFollowedException("A component has primitive type "
592                                                + type.getClass().getName() + " but the profile defines subcomponents"));
593                        }
594                }
595
596                return exList;
597        }
598
599        /** Tests for extra components (ie any not defined in the profile) */
600        protected void checkExtraComponents(Composite comp, int numInProfile, List<HL7Exception> exList)
601                        throws ProfileException {
602                StringBuilder extra = new StringBuilder();
603                for (int i = numInProfile; i < comp.getComponents().length; i++) {
604                        try {
605                                String s = PipeParser.encode(comp.getComponent(i), enc);
606                                if (s.length() > 0) {
607                                        extra.append(s).append(enc.getComponentSeparator());
608                                }
609                        } catch (DataTypeException de) {
610                                throw new ProfileException("Problem testing against profile", de);
611                        }
612                }
613
614                if (extra.length() > 0) {
615                        exList.add(new XElementPresentException(
616                                        "The following components are not defined in the profile: " + extra.toString()));
617                }
618
619        }
620
621        public static void main(String args[]) {
622
623                if (args.length != 2) {
624                        System.out.println("Usage: DefaultValidator message_file profile_file");
625                        System.exit(1);
626                }
627
628                DefaultValidator val = new DefaultValidator();
629                try {
630                        String msgString = loadFile(args[0]);
631                        Parser parser = new GenericParser();
632                        Message message = parser.parse(msgString);
633
634                        String profileString = loadFile(args[1]);
635                        ProfileParser profParser = new ProfileParser(true);
636                        RuntimeProfile profile = profParser.parse(profileString);
637
638                        HL7Exception[] exceptions = val.validate(message, profile.getMessage());
639
640                        System.out.println("Exceptions: ");
641                        for (int i = 0; i < exceptions.length; i++) {
642                                System.out.println((i + 1) + ". " + exceptions[i].getMessage());
643                        }
644                } catch (Exception e) {
645                        e.printStackTrace();
646                }
647        }
648
649        /** loads file at the given path */
650        private static String loadFile(String path) throws IOException {
651                File file = new File(path);
652                // char[] cbuf = new char[(int) file.length()];
653                BufferedReader in = new BufferedReader(new FileReader(file));
654                StringBuffer buf = new StringBuffer(5000);
655                int c;
656                while ((c = in.read()) != -1) {
657                        buf.append((char) c);
658                }
659                // in.read(cbuf, 0, (int) file.length());
660                in.close();
661                // return String.valueOf(cbuf);
662                return buf.toString();
663        }
664
665}