/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openehealth.ipf.commons.ihe.xds.core.metadata;

import ca.uhn.hl7v2.model.Composite;
import ca.uhn.hl7v2.model.Primitive;
import ca.uhn.hl7v2.model.Type;
import ca.uhn.hl7v2.model.v25.datatype.*;
import ca.uhn.hl7v2.parser.EncodingCharacters;
import ca.uhn.hl7v2.parser.Escape;
import ca.uhn.hl7v2.parser.PipeParser;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * A renderer of HL7 v2 elements which considers XDS-specific
 * requirements regarding required and prohibited fields
 * as prescribed in the ITI TF Volume 3 Chapter 4.
 *
 * @author Dmytro Rud
 */
abstract public class XdsHl7v2Renderer {

    /**
     * Invoker of the private method {@link PipeParser#stripExtraDelimiters(String, char)}.
     */
    private static final Method STRIP_EXTRA_DELIMITERS;

    static {
        try {
            STRIP_EXTRA_DELIMITERS = PipeParser.class.getDeclaredMethod("stripExtraDelimiters", String.class, char.class);
            STRIP_EXTRA_DELIMITERS.setAccessible(true);
        } catch (NoSuchMethodException e) {
            throw new LinkageError("HAPI 1.2 is expected in the class path");
        }
    }


    /**
     * Encoding characters for HL7 v2 messages.
     */
    public static final EncodingCharacters ENCODING_CHARACTERS =
            new EncodingCharacters('|', '^', '~', '\\', '&');

    /**
     * Numbers of fields of HL7 v2 composite types which are allowed to appear in XDS-specific
     * string representation.  Note that the numbers are ZERO-based!  When some type is not
     * mentioned here, its all sub-fields will be allowed.
     */
    private static final Map<Class<? extends Type>, Collection<Integer>> INCLUSIONS;

    private static void addInclusion(Class<? extends Type> typeClass, Integer... fieldNumbers) {
        INCLUSIONS.put(typeClass, Arrays.asList(fieldNumbers));
    }

    static {
        INCLUSIONS = new HashMap<Class<? extends Type>, Collection<Integer>>();
        addInclusion(CE.class,  0, 2);
        addInclusion(CX.class,  0, 3);
        addInclusion(HD.class,  1, 2);
        addInclusion(XON.class, 0, 5, 9);
    }


    /**
     * Invokes the private method {@link PipeParser#stripExtraDelimiters(String, char)}.
     * @param source
     *      source HL7 v2 string.
     * @param delimiter
     *      delimiter character to be stripped.
     * @return
     *      source string with stripped redundant trailing delimiter characters.
     */
    protected static String stripExtraDelimiters(String source, char delimiter) {
        try {
            return (String) STRIP_EXTRA_DELIMITERS.invoke(null, source, delimiter);
        } catch (IllegalAccessException e) {
            throw new LinkageError("HAPI 1.2 is expected in the class path");
        } catch (InvocationTargetException e) {
            throw new LinkageError("HAPI 1.2 is expected in the class path");
        }
    }


    /**
     * Encodes the given HL7 v2 composite field using XDS-specific
     * rules regarding required and prohibited sub-fields.
     * @param composite
     *      HL7 v2 composite field to be rendered.
     * @return
     *      String representation of the given HL7 v2 composite field.
     */
    public static String encode(Composite composite) {
        return encodeComposite(composite, ENCODING_CHARACTERS.getComponentSeparator());
    }


    protected static String encodeComposite(Composite composite, char delimiter) {
        StringBuilder sb = new StringBuilder();
        Type[] fields = composite.getComponents();
        Collection<Integer> inclusions = INCLUSIONS.get(composite.getClass());

        for (int i = 0; i < fields.length; ++i) {
            if ((inclusions == null) || inclusions.contains(i)) {
                if (fields[i] instanceof Composite) {
                    sb.append(encodeComposite((Composite) fields[i], ENCODING_CHARACTERS.getSubcomponentSeparator()));
                } else if (fields[i] instanceof Primitive) {
                    sb.append(encodePrimitive((Primitive) fields[i]));
                } else {
                    // actually, this line should be unreachable
                    throw new IllegalStateException("Don't know how to handle " + fields[i]);
                }
            }
            sb.append(delimiter);
        }

        return stripExtraDelimiters(sb.toString(), delimiter);
    }


    public static String encodePrimitive(Primitive p) {
        String value = p.getValue();
        return (value == null) ? "" : Escape.escape(value, ENCODING_CHARACTERS);
    }

}

@@ -909,124 +909,44 @@
 ort 
-ca.uhn.hl7v2.parser.PipeParser;%0A%0Aimport java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method
+org.apache.commons.lang3.StringUtils
 ;%0Aim
@@ -1213,519 +1213,8 @@
  %7B%0A%0A
-    /**%0A     * Invoker of the private method %7B@link PipeParser#stripExtraDelimiters(String, char)%7D.%0A     */%0A    private static final Method STRIP_EXTRA_DELIMITERS;%0A%0A    static %7B%0A        try %7B%0A            STRIP_EXTRA_DELIMITERS = PipeParser.class.getDeclaredMethod(%22stripExtraDelimiters%22, String.class, char.class);%0A            STRIP_EXTRA_DELIMITERS.setAccessible(true);%0A        %7D catch (NoSuchMethodException e) %7B%0A            throw new LinkageError(%22HAPI 1.2 is expected in the class path%22);%0A        %7D%0A    %7D%0A%0A%0A
     
@@ -2185,770 +2185,112 @@
     
-/**%0A     * Invokes the private method %7B@link PipeParser#stripExtraDelimiters(String, char)%7D.%0A     * @param source%0A     *      source HL7 v2 string.%0A     * @param delimiter%0A     *      delimiter character to be stripped.%0A     * @return%0A     *      source string with stripped redundant trailing delimiter characters.%0A     */%0A    protected static String stripExtraDelimiters(String source, char delimiter) %7B%0A        try %7B%0A            return (String) STRIP_EXTRA_DELIMITERS.invoke(null, source, delimiter);%0A        %7D catch (IllegalAccessException e) %7B%0A            throw new LinkageError(%22HAPI 1.2 is expected in the class path%22);%0A        %7D catch (InvocationTargetException e) %7B%0A            throw new LinkageError(%22HAPI 1.2 is expected in the class path%22);%0A        %7D
+private XdsHl7v2Renderer() %7B%0A        throw new IllegalStateException(%22cannot instantiate helper class%22);
 %0A   
@@ -3711,28 +3711,28 @@
 urn 
-stripExtraDelimiters
+StringUtils.stripEnd
 (sb.
@@ -3743,16 +3743,31 @@
 ring(), 
+String.valueOf(
 delimite
@@ -3764,24 +3764,25 @@
 f(delimiter)
+)
 ;%0A    %7D%0A%0A%0A  

use Apache Commons instead of Java Reflection