src/org/myorg/myscclient/ClientUtilities.java
package org.myorg.myscclient;

import com.im.commons.db.ddl.DBDatabaseInfo;
import com.im.commons.progress.DFEnvironmentRO;
import com.im.commons.progress.DFEnvironmentRW;
import com.im.commons.progress.DFFeedback;
import com.im.commons.progress.DFLock;
import com.im.commons.progress.EnvUtils;
import com.im.df.api.capabilities.DBFieldCapability;
import com.im.df.api.capabilities.DBFieldCapability.DBColumn;
import com.im.df.api.ddl.DFEntity;
import com.im.df.api.ddl.DFField;
import com.im.df.api.support.DFNewType;
import com.im.df.api.support.DFNewTypeWellKnownOptions;
import com.im.df.api.support.DFNewTypeWellKnownOptions.DBColumnInfo;
import com.im.df.api.util.DIFUtilities;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.openide.util.NbBundle;

/**
 *
 */
final class ClientUtilities {

    static final String CD_ID = "CD_ID"; // NOI18N
    private static final String[] COLUMNS_FOR_EXPORT = { CD_ID, "DONORS", "ACCEPTORS", "DB_REGID" }; // NOI18N
    private static final int BATCH_SIZE_TO_REPORT = 50;

    private ClientUtilities() {
    }

    /**
     * Find field by its column's name.
     *
     * @param entity Parent entity
     * @param columnName Name of the field
     * @return the field or null (if it wasn't found).
     */
    static DFField findField(DFEntity entity, String columnName) {
        for (DFField f: entity.getFields().getItems()) {
            String fieldColName = getColumnName(f);
            if ((fieldColName != null) &&  (fieldColName.equalsIgnoreCase(columnName))) {
                return f;
            }
        }
        return null;
    }

    /**
     * Return name of DB column for this field. Can return null if field is not based on database column.
     */
    static String getColumnName(DFField field) {
        DBFieldCapability dbField = DIFUtilities.findCapability(field, DBFieldCapability.class);
        if (dbField != null) {
            List<DBColumn> columns = dbField.getColumns();
            if (columns.size() == 1) {
                // it's expected that most fields have exactly one column
                return columns.get(0).getColumnName();
            }
        }
        return null;
    }

    /** Create a new field in database if returned sdf contains a new column.
     *
     * @param entity Parent entity
     * @param fieldName Name of the new field
     * @param implType Field implementation constant
     * @param columnType Preferred column type (if it's available on this DB). If it's null default column type is chosen.
     * @param env Environment - to report feedback
     * @return Newly created field
     */
    static DFField createNewField(DFEntity entity, String fieldName, String implType,
            DBDatabaseInfo.ColumnSQLType columnType, DFEnvironmentRO env) {

        env.getFeedback().addMessage(DFFeedback.Type.INFO,
                NbBundle.getMessage(ClientUtilities.class, "MSG_AddingNewField", fieldName), null);
        DFNewType<DFField> nt = DIFUtilities.findAppropriateNewType(entity.getFields().getNewTypes(), false, implType);
        assert (nt.getOptions() instanceof DFNewTypeWellKnownOptions.NewDBField);
        DFNewTypeWellKnownOptions.NewDBField nto = (DFNewTypeWellKnownOptions.NewDBField) nt.getOptions();
        nto.setNewDFItemNameSafe(fieldName);

        if (columnType != null) {
            // Try to find column type if it's supported on this DB
            DBColumnInfo column = nto.getColumns()[0];
            DBDatabaseInfo.NativeType[] allNativeTypeDefinitions = column.getAllNativeTypeDefinitions();
            for (DBDatabaseInfo.NativeType type: allNativeTypeDefinitions) {
                if (type.geColumnType() == columnType) {
                    column.setNativeTypeDefinition(type);
                    break;
                }
            }
        }

        if (!nto.isValid()) {
            String msg = NbBundle.getMessage(ClientUtilities.class, "MSG_ErrorWhenCreatingField", nto.getErrorMessage());
            throw new IllegalStateException(msg);
        }

        DFLock lock = DIFUtilities.getLockable(entity).obtainLock(
                NbBundle.getMessage(ClientUtilities.class, "MSG_CreatingField", fieldName));

        try {
            DFEnvironmentRW envRW = EnvUtils.createRWFromRO(env, lock);
            DFField f = nt.create(envRW).iterator().next();
            env.getFeedback().addMessage(DFFeedback.Type.INFO,
                    NbBundle.getMessage(ClientUtilities.class, "MSG_AddingNewFieldDone", fieldName), null);
            return f;
        } finally {
            if (lock != null) {
                lock.release();
            }
        }
    }

    static void reportProgress(DFEnvironmentRO env, String msgKey, int progress) {
        env.getFeedback().progress(progress);
        if (msgKey != null) {
            String msg = NbBundle.getMessage(ClientUtilities.class, msgKey);
            env.getFeedback().addMessage(DFFeedback.Type.PROGRESS_MESSAGE, msg, null);
            env.getFeedback().addMessage(DFFeedback.Type.INFO, msg, null);
        }
    }

    static void reportProgressDetail(DFEnvironmentRO env, String msgKey, int current, int total, int min, int max) {
        int progress = min + (int) (((float) current / (float) total) * (max - min));
        env.getFeedback().progress(progress);
        if ((current == total) || (current % BATCH_SIZE_TO_REPORT == 0)) {
            if (msgKey != null) {
                String msg = NbBundle.getMessage(ClientUtilities.class, msgKey, current, total);
                env.getFeedback().addMessage(DFFeedback.Type.INFO, msg, null);
            }
        }
    }

    /** Filter fields which should be exported and sent to Validate web service
     *
     * @param entity Parent entity
     * @return List of fields to be sent to server
     */
    static List<DFField> prepareFieldsForExport(DFEntity entity) {
        List<DFField> res = new ArrayList<DFField>();
        for (String fieldName: COLUMNS_FOR_EXPORT) {
            DFField f = findField(entity, fieldName);
            if (f != null) {
                res.add(f);
            }
        }
        return res;
    }

    static class StringBufferOutputStream extends OutputStream {
        private StringBuffer strBuffer = new StringBuffer();

        @Override
        public void write(int b) throws IOException {
            strBuffer.append((char) b);
        }

        public String asString() {
            return strBuffer.toString();
        }
    }
}