/*
 * Copyright 2009 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.atna;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A generic data structure used to store information pieces 
 * needed for ATNA auditing.
 * 
 * @author Dmytro Rud
 */
public class AuditDataset {
    private static final transient Log LOG = LogFactory.getLog(AuditDataset.class);
    
    // whether we audit on server (true) or on client (false)
    private final boolean serverSide;


    /**
     * Constructor.
     * 
     * @param serverSide
     *            specifies whether this audit dataset will be used on the
     *            server side (<code>true</code>) or on the client side 
     *            (<code>false</code>)
     */
    public AuditDataset(boolean serverSide) {
        this.serverSide = serverSide;
    }
    
    public boolean isServerSide() {
        return serverSide;
    }

    
    /**
     * <i>"What you see is what I get"</i>&nbsp;&mdash; returns a string that
     * consists from all fields available through getter methods.
     * 
     * @return string representation of this audit dataset.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");

        try {
            Method[] methods = this.getClass().getMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                Class<?> methodReturnType = method.getReturnType();

                if ((methodName.startsWith("get") || methodName.startsWith("is"))
                        && (method.getParameterTypes().length == 0)) 
                {
                    sb.append("\n    ")
                      .append(method.getName())
                      .append(" -> ");

                    if (methodReturnType == String[].class) {
                        String[] result = (String[]) method.invoke(this);
                        if(result != null) {
                            for (int i = 0; i < result.length; ++i) {
                                sb.append((i == 0) ? "{" : ", ")
                                  .append(result[i]);
                            }
                            sb.append('}');
                        } else {
                            sb.append("null");
                        }

                    } else {
                        sb.append(method.invoke(this));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.append("\n]").toString();
    }

    /**
     * Checks whether this audit dataset contains non-null values in the fields
     * from the given list.
     * 
     * @param fieldNames
     *            a list of field names with first letter capitalized, e.g.
     *            "Address"
     * @param positiveCheck
     *            {@code true} when the given fields must be present;
     *            {@code false} when they must be absent.
     * @return a set of names of the fields which do not match the given
     *         condition (i.e. are absent when they must be present, and vice
     *         versa).
     * @throws Exception
     *             on reflection errors
     */
    public Set<String> checkFields(String[] fieldNames, boolean positiveCheck) throws Exception {
        Set<String> result = new HashSet<String>();

        for (String fieldName : fieldNames) {
            Method m = getClass().getMethod("get" + fieldName);
            Object o = m.invoke(this);
            if ((o == null) == positiveCheck) {
                result.add(fieldName);
            }
        }

        return result;
    }


    /**
     * Checks whether this audit dataset misses some fields, when 
     * yes&nbsp;&mdash; writes a corresponding message into the log.
     * 
     * @param fieldNames
     *            a list of field names with first letter capitalized, e.g.
     *            "Address"
     * @param positiveCheck
     *            {@code true} when the given fields must be present;
     *            {@code false} when they must be absent.
     * @param allowIncompleteAudit
     *            {@code true} when the incomplete audit records are allowed;
     *            {@code false} otherwise.
     * @param log
     *            logger to send the error message to.
     * @return
     *            {@code true} when the auditing can be performed;
     *            {@code false} otherwise.
     * @throws Exception
     *            on reflection errors.
     */
    public boolean isAuditingPossible(
            String[] fieldNames, 
            boolean positiveCheck,
            boolean allowIncompleteAudit) throws Exception
    {
        Set<String> missing = checkFields(fieldNames, positiveCheck);
        if(! missing.isEmpty()) {
            StringBuilder sb = new StringBuilder("Missing audit fields: ");
            for(String fieldName : missing) {
                sb.append(fieldName).append(", ");
            }
            sb.append(allowIncompleteAudit ? 
                "but incomplete audit is allowed, so we'll perform it." :
                "auditing not possible.");
            LOG.error(sb.toString());
        }
        
        return missing.isEmpty() || allowIncompleteAudit;
    }
    
}

@@ -760,24 +760,136 @@
 til.Set;%0D%0A%0D%0A
+import org.apache.commons.lang.builder.ToStringBuilder;%0D%0Aimport org.apache.commons.lang.builder.ToStringStyle;%0D%0A
 import org.a
@@ -1759,22 +1759,16 @@
    %7D%0D%0A%0D%0A
-    %0D%0A
     /**%0D
@@ -1924,1561 +1924,144 @@
    *
- %0D%0A     * @return string representation of this audit dataset.%0D%0A     */%0D%0A    @Override%0D%0A    public String toString() %7B%0D%0A        StringBuilder sb = new StringBuilder(%22%5B%22);%0D%0A%0D%0A        try %7B%0D%0A            Method%5B%5D methods = this.getClass().getMethods();%0D%0A            for (Method method : methods) %7B%0D%0A                String methodName = method.getName();%0D%0A                Class%3C?%3E methodReturnType = method.getReturnType();%0D%0A%0D%0A                if ((methodName.startsWith(%22get%22) %7C%7C methodName.startsWith(%22is%22))%0D%0A                        && (method.getParameterTypes().length == 0)) %0D%0A                %7B%0D%0A                    sb.append(%22%5Cn    %22)%0D%0A                      .append(method.getName())%0D%0A                      .append(%22 -%3E %22);%0D%0A%0D%0A                    if (methodReturnType == String%5B%5D.class) %7B%0D%0A                        String%5B%5D result = (String%5B%5D) method.invoke(this);%0D%0A                        if(result != null) %7B%0D%0A                            for (int i = 0; i %3C result.length; ++i) %7B%0D%0A                                sb.append((i == 0) ? %22%7B%22 : %22, %22)%0D%0A                                  .append(result%5Bi%5D);%0D%0A                            %7D%0D%0A                            sb.append('%7D');%0D%0A                        %7D else %7B%0D%0A                            sb.append(%22null%22);%0D%0A                        %7D%0D%0A%0D%0A                    %7D else %7B%0D%0A                        sb.append(method.invoke(this));%0D%0A                    %7D%0D%0A                %7D%0D%0A            %7D%0D%0A        %7D catch (Exception e) %7B%0D%0A            e.printStackTrace();%0D%0A        %7D%0D%0A        return sb.append(%22%5Cn%5D%22).toString(
+/%0D%0A    @Override%0D%0A    public String toString() %7B%0D%0A        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE
 );%0D%0A

use Apache Commons to render the audit dataset