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 "RuleTypeBuilder.java".  Description:
010 "RuleBuilder that determines which kind of rule shall be built"
011
012 The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 2004.  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 */
026package ca.uhn.hl7v2.validation.builder;
027
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.HashSet;
033import java.util.List;
034import java.util.Set;
035
036import ca.uhn.hl7v2.HL7Exception;
037import ca.uhn.hl7v2.Version;
038import ca.uhn.hl7v2.model.Message;
039import ca.uhn.hl7v2.util.Terser;
040import ca.uhn.hl7v2.validation.MessageRule;
041import ca.uhn.hl7v2.validation.PrimitiveTypeRule;
042import ca.uhn.hl7v2.validation.Rule;
043import ca.uhn.hl7v2.Severity;
044import ca.uhn.hl7v2.validation.ValidationException;
045import ca.uhn.hl7v2.validation.impl.AbstractMessageRule;
046import ca.uhn.hl7v2.validation.impl.RuleBinding;
047import ca.uhn.hl7v2.validation.impl.RuleSupport;
048
049/**
050 * Defines the type of rule to be built.
051 * <p/>
052 * The recursive type parameter allows the builder methods common to all subclasses (e.g.
053 * {@link #refersToSection}, {@link #active}, {@link #test}) to return their specific builder type.
054 *
055 * @author Christian Ohr
056 */
057@SuppressWarnings("serial")
058public class RuleTypeBuilder<S extends RuleTypeBuilder<S, T>, T extends Rule<?>> extends
059        BuilderSupport {
060
061    private List<RuleBinding<? extends Rule<?>>> rules = new ArrayList<RuleBinding<? extends Rule<?>>>();
062    private Set<Version> versions;
063    private String description;
064    private String sectionReference;
065    private boolean active = true;
066    private Severity severity = Severity.ERROR;
067
068    protected RuleTypeBuilder() {
069        super();
070    }
071
072    protected RuleTypeBuilder(List<RuleBinding<? extends Rule<?>>> rules, Set<Version> versions) {
073        super();
074        if (versions.size() == 0)
075            throw new IllegalArgumentException("Must specify a version");
076        this.rules = rules;
077        this.versions = versions;
078    }
079
080    protected RuleTypeBuilder(List<RuleBinding<? extends Rule<?>>> rules, Version... versions) {
081        super();
082        if (versions.length == 0)
083            throw new IllegalArgumentException("Must specify a version");
084        this.rules = rules;
085        this.versions = new HashSet<Version>(Arrays.asList(versions));
086    }
087
088    @SuppressWarnings("unchecked")
089    protected S instance() {
090        return (S) this;
091    }
092
093    protected List<RuleBinding<? extends Rule<?>>> getRules() {
094        return rules;
095    }
096
097    protected T prepareRule(T rule) {
098        if (rule instanceof RuleSupport) {
099            RuleSupport<?> rs = (RuleSupport<?>) rule;
100            if (description != null) rs.setDescription(description);
101            if (sectionReference != null) rs.setSectionReference(sectionReference);
102            rs.setSeverity(severity);
103        }
104        return rule;
105    }
106
107    /**
108     * Adds a description to the rule
109     *
110     * @param description description
111     * @return this instance to build more rules
112     */
113    public S description(String description) {
114        this.description = description;
115        return instance();
116    }
117
118    /**
119     * Adds a HL7 section reference to a rule
120     *
121     * @param sectionReference the section in the HL7 specification
122     * @return this instance to build more rules
123     */
124    public S refersToSection(String sectionReference) {
125        this.sectionReference = sectionReference;
126        return instance();
127    }
128
129    /**
130     * Sets the severity of the rule
131     *
132     * @param severity the the severity of the rule
133     * @return this instance to build more rules
134     */
135    public S severity(Severity severity) {
136        this.severity = severity;
137        return instance();
138    }
139
140    /**
141     * Marks the rule as being active (default) or inactive
142     *
143     * @param active true if this rule shall be active
144     * @return this instance to build more rules
145     */
146    public S active(boolean active) {
147        this.active = active;
148        return instance();
149    }
150
151    /**
152     * Adds the specified rule to the set of rules.
153     *
154     * @param rule the rule to be tested
155     * @return this instance to build more rules
156     */
157    public S test(T rule) {
158        addRuleBindings(rule);
159        return instance();
160    }
161
162    /**
163     * Builds {@link PrimitiveTypeRule}s for the specified types
164     *
165     * @param type an array of types
166     * @return this instance to continue building rules
167     */
168    public PrimitiveRuleBuilder primitive(String... type) {
169        if (type.length == 0) {
170            throw new IllegalArgumentException("Must specify a type");
171        }
172        return new PrimitiveRuleBuilder(rules, versions, new HashSet<String>(Arrays.asList(type)));
173    }
174
175    /**
176     * Builds {@link MessageRule}s for the specified event types and triggers
177     *
178     * @param eventType      Event type, e.g. "ADT", or "*" for all types
179     * @param triggerEvents, e.g. "A01" or "A01,A04", or "*" for all trigger events
180     * @return this instance to continue building rules
181     */
182    public MessageRuleBuilder message(String eventType, String... triggerEvents) {
183        return new MessageRuleBuilder(rules, versions, eventType, triggerEvents);
184    }
185
186    /**
187     * Builds {@link MessageRule}s for event types and triggers to be specified
188     * using the returned MessageExpressionBuilder.
189     *
190     * @return MessageExpressionBuilder instance to continue building rules
191     */
192    public MessageExpressionBuilder message() {
193        return new MessageExpressionBuilder();
194    }
195
196    /**
197     * Builds {@link MessageRule}s for the specified encoding
198     *
199     * @param encoding "XML" or "VB"
200     * @return this instance to continue building rules
201     */
202    public EncodingRuleBuilder encoding(String encoding) {
203        return new EncodingRuleBuilder(rules, versions, encoding);
204    }
205
206    /**
207     * Add {@link RuleBinding}s for the rule that have been built
208     *
209     * @param rule the rule for which bindings shall be added
210     */
211    protected void addRuleBindings(T rule) {
212        if (Version.allVersions(versions)) {
213            // Save some bindings when all HL7 versions are affected
214            rules.addAll(getRuleBindings(rule, "*"));
215        } else {
216            for (Version version : versions) {
217                rules.addAll(getRuleBindings(rule, version.getVersion()));
218            }
219        }
220    }
221
222    /**
223     * Builder implementation must overwrite this method to return all {@link RuleBinding}s for
224     * rules that have been built.
225     *
226     * @param rule    the rule for which bindings shall be retrieved
227     * @param version the HL7 version for which bindings shall be retrieved
228     * @return a collection of {@link RuleBinding}s
229     */
230    @SuppressWarnings("unchecked")
231    protected Collection<RuleBinding<T>> getRuleBindings(T rule, String version) {
232        return (Collection<RuleBinding<T>>) Collections.EMPTY_LIST;
233    }
234
235    protected Collection<RuleBinding<T>> activate(Collection<RuleBinding<T>> bindings) {
236        for (RuleBinding<T> ruleBinding : bindings) {
237            ruleBinding.setActive(active);
238        }
239        return bindings;
240    }
241
242    // for tests only
243    Set<Version> getVersions() {
244        return versions;
245    }
246
247    /**
248     * Helper builder when the events are not given explicitly but in form of an expression.
249     */
250    public class MessageExpressionBuilder {
251
252        /**
253         * Applies {@link MessageRule}s for all event types and trigger events
254         *
255         * @return rule builder
256         */
257        public MessageRuleBuilder all() {
258            return new MessageRuleBuilder(rules, versions, "*", "*");
259        }
260
261        /**
262         * Applies {@link MessageRule}s for all trigger events of a given event type
263         *
264         * @param eventType event type, e.g. "ADT"
265         * @return rule builder
266         */
267        public MessageRuleBuilder allOfEventType(String eventType) {
268            return new MessageRuleBuilder(rules, versions, eventType, "*");
269        }
270
271        /**
272         * Applies a {@link MessageRule} for all event types and trigger events, checking
273         * whether the message is of the specified event type and trigger event(s)
274         *
275         * @param triggerEvents trigger events, e.g. "A01", "A04"
276         * @return rule builder
277         */
278        public MessageRuleBuilder rejectOtherThan(final String... triggerEvents) {
279            final Set<String> triggers = new HashSet<String>(Arrays.asList(triggerEvents));
280            return all().test(new AbstractMessageRule() {
281                public ValidationException[] apply(Message message) {
282                    try {
283                        Terser t = new Terser(message);
284                        String eventType = t.get("MSH-9-1");
285                        String triggerEvent = t.get("MSH-9-2");
286                        return triggers.contains(triggerEvent) ?
287                                passed() :
288                                failed(eventType + "^" + triggerEvent + " is not accepted");
289                    } catch (HL7Exception e) {
290                        return failed(e);
291                    }
292                }
293            });
294
295        }
296
297    }
298}