View Javadoc
1   /*
2    The contents of this file are subject to the Mozilla Public License Version 1.1
3    (the "License"); you may not use this file except in compliance with the License.
4    You may obtain a copy of the License at http://www.mozilla.org/MPL/
5    Software distributed under the License is distributed on an "AS IS" basis,
6    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
7    specific language governing rights and limitations under the License.
8   
9    The Original Code is "Unmodifiable.java".  Description:
10   "Factory for unmodifiable wrappers of model classes"
11  
12   The Initial Developer of the Original Code is University Health Network. Copyright (C)
13   2015.  All Rights Reserved.
14  
15   Contributor(s): ______________________________________.
16  
17   Alternatively, the contents of this file may be used under the terms of the
18   GNU General Public License (the "GPL"), in which case the provisions of the GPL are
19   applicable instead of those above.  If you wish to allow use of your version of this
20   file only under the terms of the GPL and not to allow others to use your version
21   of this file under the MPL, indicate your decision by deleting  the provisions above
22   and replace  them with the notice and other provisions required by the GPL License.
23   If you do not delete the provisions above, a recipient may use your version of
24   this file under either the MPL or the GPL.
25   */
26  
27  package ca.uhn.hl7v2.model;
28  
29  import ca.uhn.hl7v2.AcknowledgmentCode;
30  import ca.uhn.hl7v2.HL7Exception;
31  import ca.uhn.hl7v2.HapiContext;
32  import ca.uhn.hl7v2.Location;
33  import ca.uhn.hl7v2.parser.Parser;
34  
35  import java.io.IOException;
36  
37  /**
38   * A static helper class that allows to obtain unmodifiable message wrappers, i.e. all modification to these wrappers
39   * result in an {@link java.lang.UnsupportedOperationException} or {@link java.lang.IllegalArgumentException}.
40   * <p/>
41   * Note that these wrappers have no HL7-specific type information, e.g. an {@link UnmodifiableMessage}
42   * just implements {@link ca.uhn.hl7v2.model.Message} but not some concrete event type. It is possible
43   * to use the {@link ca.uhn.hl7v2.util.Terser}, generic Getter methods, message iterators or visitors.
44   * All structures or types returned by these methods will be unmodifiable as well.
45   * <p/>
46   * Also note that the original message does not automatically become immutable.
47   */
48  public final class Unmodifiable {
49  
50      private Unmodifiable() {
51      }
52  
53  
54      /**
55       * Returns an unmodifiable wrapper around the message. When accessing structures or types of the
56       * {@link UnmodifiableMessage}, they will be unmodifiable as well. Copying these message parts into a regular
57       * message should therefore be done using {@link ca.uhn.hl7v2.util.DeepCopy}.
58       *
59       * @param msg message to be wrapped
60       * @return unmodifiable message wrapper
61       */
62      public static Message/ca/uhn/hl7v2/model/Message.html#Message">Message unmodifiableMessage(Message msg) {
63          return isUnmodifiable(msg) ? msg : new UnmodifiableMessage(msg);
64      }
65  
66      /**
67       * Parses the string to an {@link UnmodifiableMessage} using the specific HapiContext. When accessing structures or types of the
68       * returned message, they will be unmodifiable as well. The returned message caches the original message string,
69       * which is returned when calling {@link Message#encode()} or {@link Message#toString()}.
70       *
71       * @param context HapiContext
72       * @param s       message string
73       * @return unmodifiable message wrapper
74       * @throws HL7Exception if parsing fails
75       */
76      public static Message unmodifiableMessage(HapiContext context, String s) throws HL7Exception {
77          Message msg = context.getGenericParser().parse(s);
78          return new UnmodifiableMessage(msg, s);
79      }
80  
81      /**
82       * Returns true if the message instance (or a part thereof) is unmodifiable
83       *
84       * @param o something HAPI-related
85       * @return true if unmodifiable
86       */
87      public static boolean isUnmodifiable(Object o) {
88          return o instanceof UnmodifiableModel;
89      }
90  
91  
92      @SuppressWarnings("unchecked")
93      private static <T extends Type> T unmodifiableType(T type) {
94          if (isUnmodifiable(type)) return type;
95          if (type instanceof Primitive) Primitive (T) new UnmodifiablePrimitive((Primitive) type);
96          if (type instanceof Composite) Composite (T) new UnmodifiableComposite((Composite) type);
97          return (T) new UnmodifiableVaries((Variable) type);
98      }
99  
100     @SuppressWarnings("unchecked")
101     private static <T extends Structure> T unmodifiableStructure(T structure) {
102         if (isUnmodifiable(structure)) return structure;
103         if (structure instanceof Message) rMessagestrong> (T) new UnmodifiableMessage((Message) structure);
104         if (structure instanceof Group) returGrouprong> (T) new UnmodifiableGroup((Group) structure);
105         return (T) new UnmodifiableSegment((Segment) structure);
106     }
107 
108     @SuppressWarnings("unchecked")
109     private static <T extends MessageVisitor> T unmodifiableVisitor(T visitor) {
110         return isUnmodifiable(visitor) ? visitor : (T) new UnmodifiableMessageVisitor(visitor);
111     }
112 
113     @SuppressWarnings("unchecked")
114     private static ExtraComponentsdel/ExtraComponents.html#ExtraComponents">ExtraComponents unmodifiableExtraComponents(ExtraComponents ec) {
115         return isUnmodifiable(ec) ? ec : new UnmodifiableExtraComponents(ec);
116     }
117 
118     /**
119      * Marker interface for unmodifiable message (parts)
120      */
121     private interface UnmodifiableModel {
122     }
123 
124 
125     private static class Delegating<S> {
126         private final S delegate;
127 
128         protected Delegating(S delegate) {
129             this.delegate = delegate;
130         }
131 
132         public S getDelegate() {
133             return delegate;
134         }
135 
136         @Override
137         public String toString() {
138             return delegate.toString();
139         }
140 
141         /**
142          * Unmodifiable structures should compare against their modifiable
143          * delegate. Otherwise a number of iterators and finders would
144          * not work properly
145          *
146          * @param o
147          * @return
148          */
149         @Override
150         public boolean equals(Object o) {
151             if (this == o) return true;
152             if (o instanceof Delegating) {
153                 Delegating that = (Delegating) o;
154                 return delegate.equals(that.delegate);
155             }
156             if (o.getClass().isAssignableFrom(delegate.getClass())) {
157                 return delegate.equals(o);
158             }
159             return false;
160         }
161 
162         @Override
163         public int hashCode() {
164             return delegate.hashCode();
165         }
166     }
167 
168     private static class UnmodifiableVisitable<S extends Visitable> extends Delegating<S> implements Visitable, UnmodifiableModel {
169 
170         public UnmodifiableVisitable(S delegate) {
171             super(delegate);
172         }
173 
174         public boolean accept(MessageVisitor visitor, Location currentLocation) throws HL7Exception {
175             return getDelegate().accept(unmodifiableVisitor(visitor), currentLocation);
176         }
177 
178         public Location/../ca/uhn/hl7v2/Location.html#Location">Location provideLocation(Location parentLocation, int index, int repetition) {
179             return getDelegate().provideLocation(parentLocation, index, repetition);
180         }
181 
182         public boolean isEmpty() throws HL7Exception {
183             return getDelegate().isEmpty();
184         }
185     }
186 
187 
188     private abstract static class UnmodifiableStructure<S extends Structure> extends UnmodifiableVisitable<S>
189             implements Structure {
190 
191         private UnmodifiableStructure(S delegate) {
192             super(delegate);
193         }
194 
195         public Message getMessage() {
196             return getDelegate().getMessage();
197         }
198 
199         public String getName() {
200             return getDelegate().getName();
201         }
202 
203         public Group getParent() {
204             return unmodifiableStructure(getDelegate().getParent());
205         }
206 
207     }
208 
209     private static class UnmodifiableSegment<S extends Segment> extends UnmodifiableStructure<S> implements Segment {
210 
211         public UnmodifiableSegment(S delegate) {
212             super(delegate);
213         }
214 
215         public String encode() throws HL7Exception {
216             return getDelegate().encode();
217         }
218 
219         public Type[] getField(int number) throws HL7Exception {
220             if (number < 1 || number > numFields()) {
221                 throw new IllegalArgumentException(String.format("Cannot add field with index %d to unmodifiable segment %s " +
222                         " - there are currently only %d fields.", number, getName(), numFields()));
223             }
224             Type[] types = getDelegate().getField(number);
225             Type[] unmodifiableTypes = new Type[types.length];
226             if (types.length > 0) {
227                 for (int i = 0; i < types.length; i++) {
228                     unmodifiableTypes[i] = unmodifiableType(types[i]);
229                 }
230             }
231             return unmodifiableTypes;
232         }
233 
234         public Type getField(int number, int rep) throws HL7Exception {
235             Type[] types = getField(number);
236             if (rep >= types.length) {
237                 throw new IllegalArgumentException(String.format("Cannot add repetition with index %d to unmodifiable field %d " +
238                         " - there are currently only %d fields.", rep, number, types.length));
239             }
240             return types[rep];
241         }
242 
243         public int getLength(int number) throws HL7Exception {
244             return getDelegate().getLength(number);
245         }
246 
247         public int getMaxCardinality(int number) throws HL7Exception {
248             return getDelegate().getMaxCardinality(number);
249         }
250 
251         public String[] getNames() {
252             return getDelegate().getNames();
253         }
254 
255         public boolean isRequired(int number) throws HL7Exception {
256             return getDelegate().isRequired(number);
257         }
258 
259         public int numFields() {
260             return getDelegate().numFields();
261         }
262 
263         public void parse(String string) {
264             throw new UnsupportedOperationException("This segment is unmodifiable");
265         }
266     }
267 
268     private static class UnmodifiableGroup<S extends Group> extends UnmodifiableStructure<S> implements Group {
269         public UnmodifiableGroup(S delegate) {
270             super(delegate);
271         }
272 
273         public Structure[] getAll(String name) throws HL7Exception {
274             Structure[] structures = getDelegate().getAll(name);
275             Structurecture[] unmodifiableStructures = new Structure[structures.length];
276             if (structures.length > 0) {
277                 for (int i = 0; i < structures.length; i++) {
278                     unmodifiableStructures[i] = unmodifiableStructure(structures[i]);
279                 }
280             }
281             return unmodifiableStructures;
282         }
283 
284         public Structure get(String name) throws HL7Exception {
285             return get(name, 0);
286         }
287 
288         /**
289          * This method does NOT append a trailing repetition, but will instead throw an {@link IndexOutOfBoundsException}
290          *
291          * @param name name of the structure
292          * @param rep  repetition (zero-based)
293          * @return element of the repeating structure
294          * @throws HL7Exception              if name does not exist
295          * @throws IndexOutOfBoundsException if repetition does not exist
296          */
297         public Structure get(String name, int rep) throws HL7Exception {
298             return getAll(name)[rep];
299         }
300 
301         public boolean isRequired(String name) throws HL7Exception {
302             return getDelegate().isRequired(name);
303         }
304 
305         public boolean isRepeating(String name) throws HL7Exception {
306             return getDelegate().isRepeating(name);
307         }
308 
309         public boolean isChoiceElement(String name) throws HL7Exception {
310             return getDelegate().isChoiceElement(name);
311         }
312 
313         public boolean isGroup(String name) throws HL7Exception {
314             return getDelegate().isGroup(name);
315         }
316 
317         public String[] getNames() {
318             return getDelegate().getNames();
319         }
320 
321         public Class<? extends Structure> getClass(String name) {
322             return getDelegate().getClass(name);
323         }
324 
325         public String addNonstandardSegment(String name) {
326             throw new UnsupportedOperationException("This group is unmodifiable");
327         }
328 
329         public String addNonstandardSegment(String name, int theIndex) {
330             throw new UnsupportedOperationException("This group is unmodifiable");
331         }
332     }
333 
334     private static class UnmodifiableMessage extends UnmodifiableGroup<Message> implements Message {
335 
336         private String originalMessage;
337 
338         public UnmodifiableMessage(Message delegate, String originalMessage) {
339             this(delegate);
340             this.originalMessage = originalMessage;
341         }
342 
343         public UnmodifiableMessage(Message delegate) {
344             super(delegate);
345         }
346 
347         public String getVersion() {
348             return getDelegate().getVersion();
349         }
350 
351         public Character getFieldSeparatorValue() throws HL7Exception {
352             return getDelegate().getFieldSeparatorValue();
353         }
354 
355         public String getEncodingCharactersValue() throws HL7Exception {
356             return getDelegate().getEncodingCharactersValue();
357         }
358 
359         public void setParser(Parser parser) {
360             throw new UnsupportedOperationException("This message is unmodifiable");
361         }
362 
363         public Parser getParser() {
364             return getDelegate().getParser();
365         }
366 
367         public void parse(String string) {
368             throw new UnsupportedOperationException("This message is unmodifiable");
369         }
370 
371         public String encode() throws HL7Exception {
372             return originalMessage != null ? originalMessage : getDelegate().encode();
373         }
374 
375         public Message generateACK() throws HL7Exception, IOException {
376             return getDelegate().generateACK();
377         }
378 
379         public Message generateACK(String theAcknowlegementCode, HL7Exception theException) throws HL7Exception, IOException {
380             return getDelegate().generateACK(theAcknowlegementCode, theException);
381         }
382 
383         public Message generateACK(AcknowledgmentCode theAcknowlegementCode, HL7Exception theException) throws HL7Exception, IOException {
384             return getDelegate().generateACK(theAcknowlegementCode, theException);
385         }
386 
387         public String printStructure() throws HL7Exception {
388             return getDelegate().printStructure();
389         }
390     }
391 
392     private abstract static class UnmodifiableType<T extends Type> extends UnmodifiableVisitable<T>
393             implements Type {
394 
395         public UnmodifiableType(T delegate) {
396             super(delegate);
397         }
398 
399         public String getName() {
400             return getDelegate().getName();
401         }
402 
403         public ExtraComponents getExtraComponents() {
404             return unmodifiableExtraComponents(getDelegate().getExtraComponents());
405         }
406 
407         public Message getMessage() {
408             return unmodifiableMessage(getDelegate().getMessage());
409         }
410 
411         public void parse(String string) {
412             throw new UnsupportedOperationException("This type is unmodifiable");
413         }
414 
415         public String encode() throws HL7Exception {
416             return getDelegate().encode();
417         }
418 
419         public void clear() {
420             throw new UnsupportedOperationException("This type is unmodifiable");
421         }
422 
423         public Location/../ca/uhn/hl7v2/Location.html#Location">Location provideLocation(Location parentLocation, int index, int repetition) {
424             return getDelegate().provideLocation(parentLocation, index, repetition);
425         }
426 
427     }
428 
429     private static class UnmodifiablePrimitive extends UnmodifiableType<Primitive> implements Primitive {
430 
431         public UnmodifiablePrimitive(Primitive delegate) {
432             super(delegate);
433         }
434 
435         public String getValue() {
436             return getDelegate().getValue();
437         }
438 
439         public void setValue(String value) {
440             throw new UnsupportedOperationException("This Primitive is unmodifiable");
441         }
442     }
443 
444     private static class UnmodifiableComposite extends UnmodifiableType<Composite> implements Composite {
445 
446         public UnmodifiableComposite(Composite delegate) {
447             super(delegate);
448         }
449 
450         public Type[] getComponents() {
451             Type[] types = getDelegate().getComponents();
452             Type[] unmodifiableTypes = new Type[types.length];
453             if (types.length > 0) {
454                 for (int i = 0; i < types.length; i++) {
455                     unmodifiableTypes[i] = unmodifiableType(types[i]);
456                 }
457             }
458             return unmodifiableTypes;
459         }
460 
461         public Type getComponent(int number) throws DataTypeException {
462             Type type = getDelegate().getComponent(number);
463             return unmodifiableType(type);
464         }
465 
466     }
467 
468     private static class UnmodifiableVaries extends UnmodifiableType<Variable> implements Variable {
469 
470         public UnmodifiableVaries(Variable delegate) {
471             super(delegate);
472         }
473 
474         public Type getData() {
475             return unmodifiableType(getDelegate().getData());
476         }
477 
478         public void setData(Type data) {
479             throw new UnsupportedOperationException("This Varies is unmodifiable");
480         }
481 
482     }
483 
484     private static class UnmodifiableExtraComponents extends ExtraComponents {
485 
486         private final ExtraComponents delegate;
487 
488         public UnmodifiableExtraComponents(ExtraComponents delegate) {
489             super(delegate.getMessage());
490             this.delegate = delegate;
491         }
492 
493         @Override
494         public int numComponents() {
495             return delegate.numComponents();
496         }
497 
498         @Override
499         public boolean isEmpty() throws HL7Exception {
500             return delegate.isEmpty();
501         }
502 
503         @Override
504         public Message getMessage() {
505             return unmodifiableMessage(delegate.getMessage());
506         }
507 
508         @Override
509         public String toString() {
510             return delegate.toString();
511         }
512 
513         @Override
514         public Variable getComponent(int comp) {
515             if (comp >= numComponents()) {
516                 throw new IllegalArgumentException(String.format(
517                         "Extra Component with index %d is not available and cannot be added to unmodifiable type", comp));
518             }
519             return unmodifiableType(delegate.getComponent(comp));
520         }
521 
522         @Override
523         void clear() {
524             throw new UnsupportedOperationException("This ExtraComponents is unmodifiable");
525         }
526     }
527 
528 
529     private static class UnmodifiableMessageVisitor extends Delegating<MessageVisitor> implements MessageVisitor, UnmodifiableModel {
530 
531 
532         public UnmodifiableMessageVisitor(MessageVisitor delegate) {
533             super(delegate);
534         }
535 
536         public boolean start(Message message) throws HL7Exception {
537             return getDelegate().start(unmodifiableMessage(message));
538         }
539 
540         public boolean end(Message message) throws HL7Exception {
541             return getDelegate().end(unmodifiableMessage(message));
542         }
543 
544         public boolean start(Group group, Location location) throws HL7Exception {
545             return getDelegate().start(unmodifiableStructure(group), location);
546         }
547 
548         public boolean end(Group group, Location location) throws HL7Exception {
549             return getDelegate().end(unmodifiableStructure(group), location);
550         }
551 
552         public boolean start(Segment segment, Location location) throws HL7Exception {
553             return getDelegate().start(unmodifiableStructure(segment), location);
554         }
555 
556         public boolean end(Segment segment, Location location) throws HL7Exception {
557             return getDelegate().end(unmodifiableStructure(segment), location);
558         }
559 
560         public boolean start(Field field, Location location) throws HL7Exception {
561             // Field is immutable anyway
562             return getDelegate().start(field, location);
563         }
564 
565         public boolean end(Field field, Location location) throws HL7Exception {
566             // Field is immutable anyway
567             return getDelegate().end(field, location);
568         }
569 
570         public boolean start(Composite type, Location location) throws HL7Exception {
571             return getDelegate().start(unmodifiableType(type), location);
572         }
573 
574         public boolean end(Composite type, Location location) throws HL7Exception {
575             return getDelegate().end(unmodifiableType(type), location);
576         }
577 
578         public boolean visit(Primitive type, Location location) throws HL7Exception {
579             return getDelegate().visit(unmodifiableType(type), location);
580         }
581     }
582 
583 }