com.collabnet.ccf.core.GenericArtifactHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.collabnet.ccf.core.GenericArtifactHelper.java

Source

/*
 * Copyright 2009 CollabNet, Inc. ("CollabNet")
 *
 * 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 com.collabnet.ccf.core;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.datatype.XMLGregorianCalendar;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.QName;
import org.dom4j.XPath;
import org.dom4j.xpath.DefaultXPath;

import com.collabnet.ccf.core.GenericArtifact.ArtifactActionValue;
import com.collabnet.ccf.core.GenericArtifact.ArtifactModeValue;
import com.collabnet.ccf.core.GenericArtifact.ArtifactTypeValue;
import com.collabnet.ccf.core.GenericArtifact.IncludesFieldMetaDataValue;
import com.collabnet.ccf.core.GenericArtifactField.FieldValueTypeValue;

/**
 * 
 * This class helps to generate and parse the schema defined generic xml
 * artifact format
 * 
 * @author jnicolai
 * 
 */
public class GenericArtifactHelper {

    private static final String SCHEMA_LOCATION_ATTRIBUTE = "schemaLocation";
    public static final DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss.SSS Z");// DateFormat.getDateTimeInstance(
    // DateFormat.FULL, DateFormat.FULL, new Locale("en"));
    public static final String ARTIFACT_ROOT_ELEMENT_NAME = "artifact";
    public static final String CCF_ARTIFACT_NAMESPACE = "http://ccf.open.collab.net/GenericArtifactV1.0";
    public static final String CCF_NAMESPACE_PREFIX = "ccf";
    public static final String SCHEMA_NAMESPACE_PREFIX = "xsi";
    public static final String SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
    public static final String CCF_SCHEMA_LOCATION = "http://ccf.open.collab.net/GenericArtifactV1.0 http://ccf.open.collab.net/files/documents/177/1972/genericartifactschema.xsd";
    private static Map<String, String> ccfNamespaceMap = Collections.singletonMap(CCF_NAMESPACE_PREFIX,
            CCF_ARTIFACT_NAMESPACE);

    public static final String ARTIFACT_ACTION = "artifactAction";
    public static final String SOURCE_ARTIFACT_LAST_MODIFICATION_DATE = "sourceArtifactLastModifiedDate";
    public static final String TARGET_ARTIFACT_LAST_MODIFICATION_DATE = "targetArtifactLastModifiedDate";
    public static final String TRANSACTION_ID = "transactionId";
    public static final String ERROR_CODE = "errorCode";
    public static final String INCLUDES_FIELD_META_DATA = "includesFieldMetaData";
    public static final String ARTIFACT_MODE = "artifactMode";
    public static final String ARTIFACT_TYPE = "artifactType";
    public static final String SOURCE_ARTIFACT_VERSION = "sourceArtifactVersion";
    public static final String TARGET_ARTIFACT_VERSION = "targetArtifactVersion";
    public static final String CONFLICT_RESOLUTION_PRIORITY = "conflictResolutionPriority";
    public static final String DEP_CHILD_SOURCE_ARTIFACT_ID = "depChildSourceArtifactId";
    public static final String DEP_CHILD_SOURCE_REPOSITORY_ID = "depChildSourceRepositoryId";
    public static final String DEP_CHILD_SOURCE_REPOSITORY_KIND = "depChildSourceRepositoryKind";
    public static final String DEP_CHILD_TARGET_ARTIFACT_ID = "depChildTargetArtifactId";;
    public static final String DEP_CHILD_TARGET_REPOSITORY_ID = "depChildTargetRepositoryId";;
    public static final String DEP_CHILD_TARGET_REPOSITORY_KIND = "depChildTargetRepositoryKind";;
    public static final String DEP_PARENT_SOURCE_ARTIFACT_ID = "depParentSourceArtifactId";
    public static final String DEP_PARENT_SOURCE_REPOSITORY_ID = "depParentSourceRepositoryId";
    public static final String DEP_PARENT_SOURCE_REPOSITORY_KIND = "depParentSourceRepositoryKind";
    public static final String DEP_PARENT_TARGET_ARTIFACT_ID = "depParentTargetArtifactId";;
    public static final String DEP_PARENT_TARGET_REPOSITORY_ID = "depParentTargetRepositoryId";;
    public static final String DEP_PARENT_TARGET_REPOSITORY_KIND = "depParentTargetRepositoryKind";;
    public static final String SOURCE_ARTIFACT_ID = "sourceArtifactId";
    public static final String SOURCE_REPOSITORY_ID = "sourceRepositoryId";
    public static final String SOURCE_REPOSITORY_KIND = "sourceRepositoryKind";
    public static final String SOURCE_SYSTEM_ID = "sourceSystemId";
    public static final String SOURCE_SYSTEM_KIND = "sourceSystemKind";
    public static final String TARGET_ARTIFACT_ID = "targetArtifactId";
    public static final String TARGET_REPOSITORY_ID = "targetRepositoryId";
    public static final String TARGET_REPOSITORY_KIND = "targetRepositoryKind";
    public static final String TARGET_SYSTEM_ID = "targetSystemId";
    public static final String TARGET_SYSTEM_KIND = "targetSystemKind";
    public static final String SOURCE_SYSTEM_TIMEZONE = "sourceSystemTimezone";
    public static final String TARGET_SYSTEM_TIMEZONE = "targetSystemTimezone";
    // public static final String SOURCE_SYSTEM_ENCODING =
    // "sourceSystemEncoding";
    // public static final String TARGET_SYSTEM_ENCODING =
    // "targetSystemEncoding";

    public static final String ARTIFACT_VERSION_FORCE_RESYNC = "-1";

    public static final String ARTIFACT_ACTION_CREATE = "create";
    public static final String ARTIFACT_ACTION_DELETE = "delete";
    public static final String ARTIFACT_ACTION_IGNORE = "ignore";
    public static final String ARTIFACT_ACTION_UPDATE = "update";
    public static final String ARTIFACT_ACTION_RESYNC = "resync";
    public static final String ARTIFACT_ACTION_UNKNOWN = "unknown";

    public static final String ARTIFACT_MODE_COMPLETE = "complete";
    public static final String ARTIFACT_MODE_CHANGED_FIELDS_ONLY = "changedFieldsOnly";
    public static final String ARTIFACT_MODE_UNKNOWN = "unknown";

    public static final String INCLUDES_FIELD_META_DATA_TRUE = "true";
    public static final String INCLUDES_FIELD_META_DATA_FALSE = "false";
    // public static final String INCLUDES_FIELD_META_DATA_UNKNOWN = "unknown";

    public static final String ARTIFACT_TYPE_DEPENDENCY = "dependency";
    public static final String ARTIFACT_TYPE_PLAIN_ARTIFACT = "plainArtifact";
    public static final String ARTIFACT_TYPE_UNKNOWN = "unknown";

    public static final String ARTIFACT_TYPE_ATTACHMENT = "attachment";

    public static final String ARTIFACT_FIELD_ELEMENT_NAME = "field";
    private static final XPath fieldSelector = new DefaultXPath(
            CCF_NAMESPACE_PREFIX + ":" + ARTIFACT_FIELD_ELEMENT_NAME);

    public static final String FIELD_ACTION = "fieldAction";

    public static final String FIELD_ACTION_APPEND = "append";
    public static final String FIELD_ACTION_DELETE = "delete";
    public static final String FIELD_ACTION_REPLACE = "replace";
    public static final String FIELD_ACTION_UNKNOWN = "unknown";

    public static final String FIELD_NAME = "fieldName";
    public static final String FIELD_TYPE = "fieldType";
    public static final String FIELD_VALUE_IS_NULL = "fieldValueIsNull";
    public static final String FIELD_VALUE_TYPE = "fieldValueType";
    public static final String FIELD_VALUE_TYPE_BASE64STRING = "Base64String";
    public static final String FIELD_VALUE_TYPE_BOOLEAN = "Boolean";
    public static final String FIELD_VALUE_TYPE_DATE = "Date";
    public static final String FIELD_VALUE_TYPE_DATETIME = "DateTime";
    public static final String FIELD_VALUE_TYPE_DOUBLE = "Double";
    public static final String FIELD_VALUE_TYPE_HTML_STRING = "HTMLString";
    public static final String FIELD_VALUE_TYPE_INTEGER = "Integer";
    public static final String FIELD_VALUE_TYPE_STRING = "String";
    private static final String FIELD_VALUE_TYPE_USER = "User";
    // private static final String FIELD_VALUE_TYPE_LIST = "List";
    // private static final String FIELD_VALUE_TYPE_MULTI_SELECT_LIST =
    // "Multi_Select_String";
    // private static final String FIELD_VALUE_TYPE_UNKNOWN = "Unknown";
    public static final String FIELD_VALUE_IS_NULL_TRUE = "true";
    public static final String FIELD_VALUE_IS_NULL_FALSE = "false";
    public static final String FIELD_VALUE_HAS_CHANGED = "fieldValueHasChanged";
    public static final String FIELD_VALUE_HAS_CHANGED_TRUE = "true";
    public static final String FIELD_VALUE_HAS_CHANGED_FALSE = "false";

    public static final String MIN_OCCURS = "minOccurs";
    public static final String MAX_OCCURS = "maxOccurs";
    public static final String NULL_VALUE_SUPPORTED = "nullValueSupported";
    public static final String ALTERNATIVE_FIELD_NAME = "alternativeFieldName";

    // translation tables
    private static HashMap<String, GenericArtifact.ArtifactModeValue> artifactModeHashMap = new HashMap<String, GenericArtifact.ArtifactModeValue>(
            2);
    private static HashMap<String, GenericArtifact.ArtifactActionValue> artifactActionHashMap = new HashMap<String, GenericArtifact.ArtifactActionValue>(
            4);
    private static HashMap<String, GenericArtifact.ArtifactTypeValue> artifactTypeHashMap = new HashMap<String, GenericArtifact.ArtifactTypeValue>(
            4);
    private static HashMap<String, GenericArtifact.IncludesFieldMetaDataValue> includesFieldMetaDataHashMap = new HashMap<String, GenericArtifact.IncludesFieldMetaDataValue>(
            3);

    private static HashMap<String, GenericArtifactField.FieldActionValue> fieldActionHashMap = new HashMap<String, GenericArtifactField.FieldActionValue>(
            3);
    private static HashMap<String, GenericArtifactField.FieldValueTypeValue> fieldValueTypeHashMap = new HashMap<String, GenericArtifactField.FieldValueTypeValue>(
            9);
    private static HashMap<String, Boolean> fieldValueIsNullHashMap = new HashMap<String, Boolean>(2);
    private static HashMap<String, Boolean> fieldValueHasChangedHashMap = new HashMap<String, Boolean>(2);

    // populate translation tables
    static {
        artifactModeHashMap.put(ARTIFACT_MODE_CHANGED_FIELDS_ONLY,
                GenericArtifact.ArtifactModeValue.CHANGEDFIELDSONLY);
        artifactModeHashMap.put(ARTIFACT_MODE_COMPLETE, GenericArtifact.ArtifactModeValue.COMPLETE);
        artifactModeHashMap.put(ARTIFACT_MODE_UNKNOWN, GenericArtifact.ArtifactModeValue.UNKNOWN);

        artifactActionHashMap.put(ARTIFACT_ACTION_CREATE, GenericArtifact.ArtifactActionValue.CREATE);
        artifactActionHashMap.put(ARTIFACT_ACTION_DELETE, GenericArtifact.ArtifactActionValue.DELETE);
        artifactActionHashMap.put(ARTIFACT_ACTION_IGNORE, GenericArtifact.ArtifactActionValue.IGNORE);
        artifactActionHashMap.put(ARTIFACT_ACTION_UPDATE, GenericArtifact.ArtifactActionValue.UPDATE);
        artifactActionHashMap.put(ARTIFACT_ACTION_RESYNC, GenericArtifact.ArtifactActionValue.RESYNC);
        artifactActionHashMap.put(ARTIFACT_ACTION_UNKNOWN, GenericArtifact.ArtifactActionValue.UNKNOWN);

        artifactTypeHashMap.put(ARTIFACT_TYPE_ATTACHMENT, GenericArtifact.ArtifactTypeValue.ATTACHMENT);
        artifactTypeHashMap.put(ARTIFACT_TYPE_DEPENDENCY, GenericArtifact.ArtifactTypeValue.DEPENDENCY);
        artifactTypeHashMap.put(ARTIFACT_TYPE_PLAIN_ARTIFACT, GenericArtifact.ArtifactTypeValue.PLAINARTIFACT);
        artifactTypeHashMap.put(ARTIFACT_TYPE_UNKNOWN, GenericArtifact.ArtifactTypeValue.UNKNOWN);

        includesFieldMetaDataHashMap.put(INCLUDES_FIELD_META_DATA_TRUE,
                GenericArtifact.IncludesFieldMetaDataValue.TRUE);
        includesFieldMetaDataHashMap.put(INCLUDES_FIELD_META_DATA_FALSE,
                GenericArtifact.IncludesFieldMetaDataValue.FALSE);
        // includesFieldMetaDataHashMap.put(INCLUDES_FIELD_META_DATA_UNKNOWN,
        // GenericArtifact.IncludesFieldMetaDataValue.UNKNOWN);

        fieldActionHashMap.put(FIELD_ACTION_APPEND, GenericArtifactField.FieldActionValue.APPEND);
        fieldActionHashMap.put(FIELD_ACTION_DELETE, GenericArtifactField.FieldActionValue.DELETE);
        fieldActionHashMap.put(FIELD_ACTION_REPLACE, GenericArtifactField.FieldActionValue.REPLACE);
        fieldActionHashMap.put(FIELD_ACTION_UNKNOWN, GenericArtifactField.FieldActionValue.UNKNOWN);

        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_BASE64STRING,
                GenericArtifactField.FieldValueTypeValue.BASE64STRING);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_BOOLEAN, GenericArtifactField.FieldValueTypeValue.BOOLEAN);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_DATE, GenericArtifactField.FieldValueTypeValue.DATE);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_DATETIME, GenericArtifactField.FieldValueTypeValue.DATETIME);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_DOUBLE, GenericArtifactField.FieldValueTypeValue.DOUBLE);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_HTML_STRING,
                GenericArtifactField.FieldValueTypeValue.HTMLSTRING);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_INTEGER, GenericArtifactField.FieldValueTypeValue.INTEGER);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_STRING, GenericArtifactField.FieldValueTypeValue.STRING);
        fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_USER, GenericArtifactField.FieldValueTypeValue.USER);
        // fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_LIST,
        // GenericArtifactField.FieldValueTypeValue.LIST);
        // fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_MULTI_SELECT_LIST,
        // GenericArtifactField.FieldValueTypeValue.MULTI_SELECT_LIST);
        // fieldValueTypeHashMap.put(FIELD_VALUE_TYPE_UNKNOWN,
        // GenericArtifactField.FieldValueTypeValue.UNKNOWN);

        fieldValueIsNullHashMap.put(FIELD_VALUE_IS_NULL_TRUE, Boolean.TRUE);
        fieldValueIsNullHashMap.put(FIELD_VALUE_IS_NULL_FALSE, Boolean.FALSE);

        fieldValueHasChangedHashMap.put(FIELD_VALUE_HAS_CHANGED_TRUE, Boolean.TRUE);
        fieldValueHasChangedHashMap.put(FIELD_VALUE_HAS_CHANGED_FALSE, Boolean.FALSE);

        // set CCF namespace in order to select nodes properly
        fieldSelector.setNamespaceURIs(ccfNamespaceMap);

    }

    /**
     * Parses an XML document that complies to the generic artifact schema and
     * represents it as a Java object
     * 
     * @param document
     *            XML representation of the generic artifact
     * @return Java object representing the artifact
     * @throws GenericArtifactParsingException
     *             exception is thrown if XML document does not comply to the
     *             schema
     */
    public static GenericArtifact createGenericArtifactJavaObject(Document document)
            throws GenericArtifactParsingException {
        GenericArtifact genericArtifact = new GenericArtifact();
        // TODO Parse the XML document and populate the Java objects
        // fetch the artifact-root-element
        Element root = getRootElement(document);
        // get and set attributes

        ArtifactActionValue artifactAction = translateAttributeValue(root, ARTIFACT_ACTION, artifactActionHashMap);
        genericArtifact.setArtifactAction(artifactAction);

        genericArtifact
                .setSourceArtifactLastModifiedDate(getAttributeValue(root, SOURCE_ARTIFACT_LAST_MODIFICATION_DATE));
        genericArtifact
                .setTargetArtifactLastModifiedDate(getAttributeValue(root, TARGET_ARTIFACT_LAST_MODIFICATION_DATE));
        // genericArtifact.setLastReadTransactionId(getAttributeValue(root,
        // ARTIFACT_LAST_READ_TRANSACTION_ID));
        genericArtifact.setErrorCode(getAttributeValue(root, ERROR_CODE));

        ArtifactModeValue artifactMode = translateAttributeValue(root, ARTIFACT_MODE, artifactModeHashMap);
        genericArtifact.setArtifactMode(artifactMode);

        ArtifactTypeValue artifactType = translateAttributeValue(root, ARTIFACT_TYPE, artifactTypeHashMap);
        genericArtifact.setArtifactType(artifactType);

        IncludesFieldMetaDataValue includesFieldMetaData = translateAttributeValue(root, INCLUDES_FIELD_META_DATA,
                includesFieldMetaDataHashMap);
        genericArtifact.setIncludesFieldMetaData(includesFieldMetaData);

        if (artifactType == GenericArtifact.ArtifactTypeValue.ATTACHMENT) {
            genericArtifact.setArtifactValue(getValue(root));
        }

        genericArtifact.setSourceArtifactVersion(getAttributeValue(root, SOURCE_ARTIFACT_VERSION));
        genericArtifact.setTargetArtifactVersion(getAttributeValue(root, TARGET_ARTIFACT_VERSION));
        genericArtifact.setConflictResolutionPriority(getAttributeValue(root, CONFLICT_RESOLUTION_PRIORITY));
        genericArtifact.setTransactionId(getAttributeValue(root, TRANSACTION_ID));
        // only read optional attributes if necessary
        if (artifactType == ArtifactTypeValue.DEPENDENCY || artifactType == ArtifactTypeValue.ATTACHMENT) {
            genericArtifact.setDepParentSourceArtifactId(getAttributeValue(root, DEP_PARENT_SOURCE_ARTIFACT_ID));
            genericArtifact
                    .setDepParentSourceRepositoryId(getAttributeValue(root, DEP_PARENT_SOURCE_REPOSITORY_ID));
            genericArtifact
                    .setDepParentSourceRepositoryKind(getAttributeValue(root, DEP_PARENT_SOURCE_REPOSITORY_KIND));
            genericArtifact.setDepParentTargetArtifactId(getAttributeValue(root, DEP_PARENT_TARGET_ARTIFACT_ID));
            genericArtifact
                    .setDepParentTargetRepositoryId(getAttributeValue(root, DEP_PARENT_TARGET_REPOSITORY_ID));
            genericArtifact
                    .setDepParentTargetRepositoryKind(getAttributeValue(root, DEP_PARENT_TARGET_REPOSITORY_KIND));
        }

        // dependencies have even more optional attributes
        if (artifactType == ArtifactTypeValue.DEPENDENCY) {
            genericArtifact.setDepChildSourceArtifactId(getAttributeValue(root, DEP_CHILD_SOURCE_ARTIFACT_ID));
            genericArtifact.setDepChildSourceRepositoryId(getAttributeValue(root, DEP_CHILD_SOURCE_REPOSITORY_ID));
            genericArtifact
                    .setDepChildSourceRepositoryKind(getAttributeValue(root, DEP_CHILD_SOURCE_REPOSITORY_KIND));
            genericArtifact.setDepChildTargetArtifactId(getAttributeValue(root, DEP_CHILD_TARGET_ARTIFACT_ID));
            genericArtifact.setDepChildTargetRepositoryId(getAttributeValue(root, DEP_CHILD_TARGET_REPOSITORY_ID));
            genericArtifact
                    .setDepChildTargetRepositoryKind(getAttributeValue(root, DEP_CHILD_TARGET_REPOSITORY_KIND));
        }

        genericArtifact.setSourceArtifactId(getAttributeValue(root, SOURCE_ARTIFACT_ID));
        genericArtifact.setSourceRepositoryId(getAttributeValue(root, SOURCE_REPOSITORY_ID));
        genericArtifact.setSourceRepositoryKind(getAttributeValue(root, SOURCE_REPOSITORY_KIND));
        genericArtifact.setSourceSystemId(getAttributeValue(root, SOURCE_SYSTEM_ID));
        genericArtifact.setSourceSystemKind(getAttributeValue(root, SOURCE_SYSTEM_KIND));
        genericArtifact.setTargetArtifactId(getAttributeValue(root, TARGET_ARTIFACT_ID));
        genericArtifact.setTargetRepositoryId(getAttributeValue(root, TARGET_REPOSITORY_ID));
        genericArtifact.setTargetRepositoryKind(getAttributeValue(root, TARGET_REPOSITORY_KIND));
        genericArtifact.setTargetSystemId(getAttributeValue(root, TARGET_SYSTEM_ID));
        genericArtifact.setTargetSystemKind(getAttributeValue(root, TARGET_SYSTEM_KIND));
        genericArtifact.setSourceSystemTimezone(getAttributeValue(root, SOURCE_SYSTEM_TIMEZONE));
        genericArtifact.setTargetSystemTimezone(getAttributeValue(root, TARGET_SYSTEM_TIMEZONE));
        // genericArtifact.setSourceSystemEncoding(getAttributeValue(root,
        // SOURCE_SYSTEM_ENCODING));
        // genericArtifact.setTargetSystemEncoding(getAttributeValue(root,
        // TARGET_SYSTEM_ENCODING));

        // now add fields
        List<Element> fields = getAllFieldElements(root);
        for (Element field : fields) {
            GenericArtifactField.FieldActionValue fieldAction = translateAttributeValue(field, FIELD_ACTION,
                    fieldActionHashMap);
            GenericArtifactField.FieldValueTypeValue fieldValueType = translateAttributeValue(field,
                    FIELD_VALUE_TYPE, fieldValueTypeHashMap);
            String fieldName = getAttributeValue(field, FIELD_NAME);
            String fieldType = getAttributeValue(field, FIELD_TYPE);
            Boolean fieldValueIsNull = translateAttributeValue(field, FIELD_VALUE_IS_NULL, fieldValueIsNullHashMap);
            Boolean fieldValueHasChanged = translateAttributeValue(field, FIELD_VALUE_HAS_CHANGED,
                    fieldValueHasChangedHashMap);
            String fieldValue = getValue(field);

            // we cannot change these two attributes later because this would
            // influence the indexing data structures for fast lookup
            GenericArtifactField genericArtifactField = genericArtifact.addNewField(fieldName, fieldType);
            genericArtifactField.setFieldAction(fieldAction);
            genericArtifactField.setFieldValueType(fieldValueType);
            genericArtifactField.setFieldValueHasChanged(fieldValueHasChanged);

            if (includesFieldMetaData.equals(GenericArtifact.IncludesFieldMetaDataValue.TRUE)) {
                String minOccurs = getAttributeValue(field, MIN_OCCURS);
                String maxOccurs = getAttributeValue(field, MAX_OCCURS);
                String nullValueSupported = getAttributeValue(field, NULL_VALUE_SUPPORTED);
                String alternativeFieldName = getAttributeValue(field, ALTERNATIVE_FIELD_NAME);

                genericArtifactField.setMinOccursValue(minOccurs);
                genericArtifactField.setMaxOccursValue(maxOccurs);
                genericArtifactField.setNullValueSupported(nullValueSupported);
                genericArtifactField.setAlternativeFieldName(alternativeFieldName);
            }

            try {
                convertFieldValue(genericArtifactField, fieldValueIsNull, fieldValueType, fieldValue);
            } catch (ParseException e) {
                throw new GenericArtifactParsingException(
                        "Value " + fieldValue + " for field-element with name " + fieldName + " and field type "
                                + fieldType + " was not convertible to an instance of value type " + fieldValueType
                                + " because: " + e.getMessage());
            }
        }

        // finally set reference to source XML document
        genericArtifact.setSourceDocument(document);
        return genericArtifact;
    }

    /**
     * Convert the string value
     * 
     * @param genericArtifactField
     *            field for which to set the new value
     * @param fieldValueIsNull
     *            if true, the field's value should be null
     * @param fieldValueType
     *            type of the field's value
     * @param value
     *            String encoded value out of XML element
     * @throws ParseException
     */
    private static void convertFieldValue(GenericArtifactField genericArtifactField, Boolean fieldValueIsNull,
            FieldValueTypeValue fieldValueType, String value) throws ParseException {
        // TODO Think carefully about all type conversions
        if (value.length() == 0 && fieldValueIsNull) {
            genericArtifactField.setFieldValue(null);
        } else
            switch (fieldValueType) {
            case BASE64STRING: {
                // TODO Better conversion?
                genericArtifactField.setFieldValue(value);
                break;
            }
            case BOOLEAN: {
                genericArtifactField.setFieldValue(Boolean.valueOf(value));
                break;
            }
            case DATE: {
                GregorianCalendar cal = new GregorianCalendar();
                synchronized (df) {
                    cal.setTime(df.parse(value));
                }
                genericArtifactField.setFieldValue(cal);
                break;
            }
            case DATETIME: {
                synchronized (df) {
                    genericArtifactField.setFieldValue(df.parse(value));
                }
                break;
            }
            case DOUBLE: {
                genericArtifactField.setFieldValue(new Double(value));
                break;
            }
            case HTMLSTRING: {
                // TODO Better conversion?
                genericArtifactField.setFieldValue(value);
                break;
            }
            case INTEGER: {
                // TODO Better conversion?
                genericArtifactField.setFieldValue(value);
                break;
            }
            case STRING: {
                genericArtifactField.setFieldValue(value);
                break;
            }
            case USER: {
                genericArtifactField.setFieldValue(value);
                break;
            }
            // case LIST: {
            // genericArtifactField.setFieldValue(value);
            // break;
            // }
            // case MULTI_SELECT_LIST: {
            // genericArtifactField.setFieldValue(value);
            // break;
            // }
            }
    }

    /**
     * Extracts all field from the
     * 
     * @param root
     *            generic artifact root-element
     * @return a list with all field-elements of the root-element
     */
    @SuppressWarnings("unchecked")
    private static List<Element> getAllFieldElements(Element root) {
        // our XPath-Expression matches only elements, so this conversion is
        // type-safe
        List<Element> fieldElements = fieldSelector.selectNodes(root);
        if (fieldElements == null)
            return new ArrayList<Element>();
        else
            return fieldElements;
    }

    /**
     * Retrieves the value of the specified attribute of the supplied XML
     * element and translate it from its String representation to type T by
     * using the lookup table
     * 
     * @param <T>
     *            type the mapped values have
     * @param element
     *            XML element
     * @param attributeName
     *            name of the attribute in question
     * @param translationTable
     *            lookup table from String to type T
     * @return mapped attribute value
     * @throws GenericArtifactParsingException
     *             thrown if attribute is not present or value not in lookup
     *             table
     */
    private static <T> T translateAttributeValue(Element element, String attributeName,
            HashMap<String, T> translationTable) throws GenericArtifactParsingException {
        String attributeValueString = getAttributeValue(element, attributeName);
        T translatedAttributeValue = translationTable.get(attributeValueString);
        if (translatedAttributeValue == null)
            throw new GenericArtifactParsingException("Non-valid value " + attributeValueString + " for attribute "
                    + attributeName + " of element " + element.getName());
        else
            return translatedAttributeValue;
    }

    /**
     * Extracts the value of the supplied attribute
     * 
     * @param element
     *            element with attribute in question
     * @param attributeName
     *            name of the attribute in question
     * @return value of the attribute in question
     * @throws GenericArtifactParsingException
     *             exception s thrown is attribute is missing
     */
    private static String getAttributeValue(Element element, String attributeName)
            throws GenericArtifactParsingException {
        // TODO Cash constructed XPath objects?
        // XPath xpath = new DefaultXPath("@" + CCF_NAMESPACE_PREFIX + ":" +
        // attributeName);
        XPath xpath = new DefaultXPath("@" + attributeName);
        xpath.setNamespaceURIs(ccfNamespaceMap);
        Node attributeNode = xpath.selectSingleNode(element);
        if (attributeNode == null)
            throw new GenericArtifactParsingException(
                    "Missing attribute: " + attributeName + " in element " + element.getName());
        else
            return attributeNode.getText();
    }

    /**
     * Extracts the artifact-root-element out of a Dom4J XML document
     * 
     * @param document
     *            XML document in question
     * @return generic artifact root-element
     * @throws GenericArtifactParsingException
     *             thrown if document is not compliant to the generic artifact
     *             schema
     */
    private static Element getRootElement(Document document) throws GenericArtifactParsingException {
        Element rootElement = document.getRootElement();
        if (!ARTIFACT_ROOT_ELEMENT_NAME.equals(rootElement.getName()))
            throw new GenericArtifactParsingException("Root-element of XML document is not named "
                    + ARTIFACT_ROOT_ELEMENT_NAME + "but " + rootElement.getName());
        if (!CCF_ARTIFACT_NAMESPACE.equals(rootElement.getNamespaceURI()))
            throw new GenericArtifactParsingException("Namespace-URI of root-element of XML document is not named "
                    + CCF_ARTIFACT_NAMESPACE + "but " + rootElement.getNamespaceURI());
        return rootElement;
    }

    /**
     * Creates a generic artifact XML representation out of the Java object
     * 
     * @param genericArtifact
     *            Java object that will be represented as XML document
     * @return XML representation of generic artifact
     */
    public static Document createGenericArtifactXMLDocument(GenericArtifact genericArtifact)
            throws GenericArtifactParsingException {
        Document document = DocumentHelper.createDocument();
        document.setXMLEncoding("UTF-8");
        // Create XML elements with attributes
        Element root = addRootElement(document, ARTIFACT_ROOT_ELEMENT_NAME, CCF_ARTIFACT_NAMESPACE);

        switch (genericArtifact.getArtifactAction()) {
        case CREATE: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_CREATE);
            break;
        }
        case DELETE: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_DELETE);
            break;
        }
        case IGNORE: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_IGNORE);
            break;
        }
        case UPDATE: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_UPDATE);
            break;
        }
        case RESYNC: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_RESYNC);
            break;
        }
        case UNKNOWN: {
            addAttribute(root, ARTIFACT_ACTION, ARTIFACT_ACTION_UNKNOWN);
            break;
        }
        default: {
            throw new GenericArtifactParsingException(
                    "Non valid value for root-attribute " + ARTIFACT_ACTION + " specified.");
        }
        }

        switch (genericArtifact.getArtifactMode()) {
        case CHANGEDFIELDSONLY: {
            addAttribute(root, ARTIFACT_MODE, ARTIFACT_MODE_CHANGED_FIELDS_ONLY);
            break;
        }
        case COMPLETE: {
            addAttribute(root, ARTIFACT_MODE, ARTIFACT_MODE_COMPLETE);
            break;
        }
        case UNKNOWN: {
            addAttribute(root, ARTIFACT_MODE, ARTIFACT_MODE_UNKNOWN);
            break;
        }
        default: {
            throw new GenericArtifactParsingException(
                    "Non valid value for root-attribute " + ARTIFACT_MODE + "specified.");
        }
        }

        ArtifactTypeValue artifactType = genericArtifact.getArtifactType();
        switch (artifactType) {
        case ATTACHMENT: {
            addAttribute(root, ARTIFACT_TYPE, ARTIFACT_TYPE_ATTACHMENT);
            String content = genericArtifact.getArtifactValue();
            // TODO BASE64 validation?
            if (content != null)
                // embed content in CDATA section
                setValue(root, content, true);
            break;
        }
        case DEPENDENCY: {
            addAttribute(root, ARTIFACT_TYPE, ARTIFACT_TYPE_DEPENDENCY);
            break;
        }
        case PLAINARTIFACT: {
            addAttribute(root, ARTIFACT_TYPE, ARTIFACT_TYPE_PLAIN_ARTIFACT);
            break;
        }
        case UNKNOWN: {
            addAttribute(root, ARTIFACT_TYPE, ARTIFACT_TYPE_UNKNOWN);
            break;
        }
        default: {
            throw new GenericArtifactParsingException(
                    "Non valid value for root-attribute " + ARTIFACT_TYPE + " specified.");
        }
        }

        switch (genericArtifact.getIncludesFieldMetaData()) {
        case TRUE: {
            addAttribute(root, INCLUDES_FIELD_META_DATA, INCLUDES_FIELD_META_DATA_TRUE);
            break;
        }
        case FALSE: {
            addAttribute(root, INCLUDES_FIELD_META_DATA, INCLUDES_FIELD_META_DATA_FALSE);
            break;
        }
        default: {
            throw new GenericArtifactParsingException(
                    "Non valid value for root-attribute " + ARTIFACT_MODE + "specified.");
        }
        }

        addAttribute(root, SOURCE_ARTIFACT_LAST_MODIFICATION_DATE,
                genericArtifact.getSourceArtifactLastModifiedDate());
        addAttribute(root, TARGET_ARTIFACT_LAST_MODIFICATION_DATE,
                genericArtifact.getTargetArtifactLastModifiedDate());
        // addAttribute(root, ARTIFACT_LAST_READ_TRANSACTION_ID, genericArtifact
        // .getLastReadTransactionId());
        addAttribute(root, ERROR_CODE, genericArtifact.getErrorCode());
        addAttribute(root, SOURCE_ARTIFACT_VERSION, genericArtifact.getSourceArtifactVersion());
        addAttribute(root, TARGET_ARTIFACT_VERSION, genericArtifact.getTargetArtifactVersion());
        addAttribute(root, CONFLICT_RESOLUTION_PRIORITY, genericArtifact.getConflictResolutionPriority());

        // only create optional attributes if necessary
        if (artifactType == ArtifactTypeValue.DEPENDENCY || artifactType == ArtifactTypeValue.ATTACHMENT) {
            addAttribute(root, DEP_PARENT_SOURCE_ARTIFACT_ID, genericArtifact.getDepParentSourceArtifactId());
            addAttribute(root, DEP_PARENT_SOURCE_REPOSITORY_ID, genericArtifact.getDepParentSourceRepositoryId());
            addAttribute(root, DEP_PARENT_SOURCE_REPOSITORY_KIND,
                    genericArtifact.getDepParentSourceRepositoryKind());
            addAttribute(root, DEP_PARENT_TARGET_ARTIFACT_ID, genericArtifact.getDepParentTargetArtifactId());
            addAttribute(root, DEP_PARENT_TARGET_REPOSITORY_ID, genericArtifact.getDepParentTargetRepositoryId());
            addAttribute(root, DEP_PARENT_TARGET_REPOSITORY_KIND,
                    genericArtifact.getDepParentTargetRepositoryKind());
        }

        // dependencies have even more optional attributes
        if (artifactType == ArtifactTypeValue.DEPENDENCY) {
            addAttribute(root, DEP_CHILD_SOURCE_ARTIFACT_ID, genericArtifact.getDepChildSourceArtifactId());
            addAttribute(root, DEP_CHILD_SOURCE_REPOSITORY_ID, genericArtifact.getDepChildSourceRepositoryId());
            addAttribute(root, DEP_CHILD_SOURCE_REPOSITORY_KIND, genericArtifact.getDepChildSourceRepositoryKind());
            addAttribute(root, DEP_CHILD_TARGET_ARTIFACT_ID, genericArtifact.getDepChildTargetArtifactId());
            addAttribute(root, DEP_CHILD_TARGET_REPOSITORY_ID, genericArtifact.getDepChildTargetRepositoryId());
            addAttribute(root, DEP_CHILD_TARGET_REPOSITORY_KIND, genericArtifact.getDepChildTargetRepositoryKind());
        }

        addAttribute(root, SOURCE_ARTIFACT_ID, genericArtifact.getSourceArtifactId());
        addAttribute(root, SOURCE_REPOSITORY_ID, genericArtifact.getSourceRepositoryId());
        addAttribute(root, SOURCE_REPOSITORY_KIND, genericArtifact.getSourceRepositoryKind());
        addAttribute(root, SOURCE_SYSTEM_ID, genericArtifact.getSourceSystemId());
        addAttribute(root, SOURCE_SYSTEM_KIND, genericArtifact.getSourceSystemKind());
        addAttribute(root, TARGET_ARTIFACT_ID, genericArtifact.getTargetArtifactId());
        addAttribute(root, TARGET_REPOSITORY_ID, genericArtifact.getTargetRepositoryId());
        addAttribute(root, TARGET_REPOSITORY_KIND, genericArtifact.getTargetRepositoryKind());
        addAttribute(root, TARGET_SYSTEM_ID, genericArtifact.getTargetSystemId());
        addAttribute(root, TARGET_SYSTEM_KIND, genericArtifact.getTargetSystemKind());
        addAttribute(root, SOURCE_SYSTEM_TIMEZONE, genericArtifact.getSourceSystemTimezone());
        addAttribute(root, TARGET_SYSTEM_TIMEZONE, genericArtifact.getTargetSystemTimezone());
        // addAttribute(root, SOURCE_SYSTEM_ENCODING, genericArtifact
        // .getSourceSystemEncoding());
        // addAttribute(root, TARGET_SYSTEM_ENCODING, genericArtifact
        // .getTargetSystemEncoding());
        addAttribute(root, TRANSACTION_ID, genericArtifact.getTransactionId());

        if (genericArtifact.getAllGenericArtifactFields() != null) {
            // now add fields
            for (GenericArtifactField genericArtifactField : genericArtifact.getAllGenericArtifactFields()) {
                Element field = addElement(root, ARTIFACT_FIELD_ELEMENT_NAME, CCF_ARTIFACT_NAMESPACE);
                switch (genericArtifactField.getFieldAction()) {
                case APPEND: {
                    addAttribute(field, FIELD_ACTION, FIELD_ACTION_APPEND);
                    break;
                }
                case DELETE: {
                    addAttribute(field, FIELD_ACTION, FIELD_ACTION_DELETE);
                    break;
                }
                case REPLACE: {
                    addAttribute(field, FIELD_ACTION, FIELD_ACTION_REPLACE);
                    break;
                }
                case UNKNOWN: {
                    addAttribute(field, FIELD_ACTION, FIELD_ACTION_UNKNOWN);
                    break;
                }
                default: {
                    throw new GenericArtifactParsingException(
                            "Non valid value for field-attribute " + FIELD_ACTION + " specified.");
                }
                }

                addAttribute(field, FIELD_NAME, genericArtifactField.getFieldName());
                addAttribute(field, FIELD_TYPE, genericArtifactField.getFieldType());
                if (genericArtifactField.getFieldValueHasChanged()) {
                    addAttribute(field, FIELD_VALUE_HAS_CHANGED, FIELD_VALUE_HAS_CHANGED_TRUE);
                } else {
                    addAttribute(field, FIELD_VALUE_HAS_CHANGED, FIELD_VALUE_HAS_CHANGED_FALSE);
                }

                if (genericArtifact.getIncludesFieldMetaData()
                        .equals(GenericArtifact.IncludesFieldMetaDataValue.TRUE)) {
                    addAttribute(field, MIN_OCCURS, genericArtifactField.getMinOccursValue());
                    addAttribute(field, MAX_OCCURS, genericArtifactField.getMaxOccursValue());
                    addAttribute(field, NULL_VALUE_SUPPORTED, genericArtifactField.getNullValueSupported());
                    addAttribute(field, ALTERNATIVE_FIELD_NAME, genericArtifactField.getAlternativeFieldName());
                }

                setFieldValue(field, genericArtifactField.getFieldValue(),
                        genericArtifactField.getFieldValueType());
            }
        }
        root.addAttribute(
                new QName(SCHEMA_LOCATION_ATTRIBUTE, new Namespace(SCHEMA_NAMESPACE_PREFIX, SCHEMA_NAMESPACE)),
                CCF_SCHEMA_LOCATION);
        return document;
    }

    /**
     * Set the content of the element
     * 
     * @param element
     *            XML element
     * @param content
     *            content, encoded as String
     * @param useCDATASection
     *            if true, embed content into CDATA-section
     */
    private static void setValue(Element element, String content, boolean useCDATASection) {
        if (useCDATASection)
            element.addCDATA(content);
        else
            element.setText(content);
    }

    /**
     * Extracts the content of the supplied element
     * 
     * @param element
     *            XML element
     * @return Content of the element encoded as String
     */
    private static String getValue(Element element) {
        return removeInvalidXmlCharacters(element.getText());
    }

    private static final String removeInvalidXmlCharacters(String input) {
        if (input == null) {
            return input;
        }
        char character;
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < input.length(); i++) {
            character = input.charAt(i);
            //see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char for valid XML character list.
            if ((character == 0x9) || (character == 0xA) || (character == 0xD)
                    || ((character >= 0x20) && (character <= 0xD7FF))
                    || ((character >= 0xE000) && (character <= 0xFFFD))
                    || ((character >= 0x10000) && (character <= 0x10FFFF))) {
                sb.append(character);
            }
        }
        return sb.toString();
    }

    /**
     * Adds the root-element to the XML document
     * 
     * @param document
     *            XML document
     * @param rootElementName
     *            name of root-element
     * @param rootElementNamespace
     *            name namespace, root-element belongs to
     * @return newly create root-element of XML document
     */
    private static Element addRootElement(Document document, String rootElementName, String rootElementNamespace) {
        return document.addElement(rootElementName, rootElementNamespace);
    }

    /**
     * Adds a sub-element to parent-element
     * 
     * @param parentElement
     *            parent element that should get a new sub-element
     * @param subElementName
     *            name of the new created sub-element
     * @param subElementNamespace
     *            namespace of the new sub-element
     * @return newly created sub-element
     */
    private static Element addElement(Element parentElement, String subElementName, String subElementNamespace) {
        return parentElement.addElement(subElementName, subElementNamespace);
    }

    /**
     * Converts native Java types to the XML format
     * 
     * @param field
     *            XML element for the field
     * @param fieldValue
     *            Java object for the field's value
     * @param fieldValueType
     *            CCF data type that should be used
     * @throws GenericArtifactParsingException
     *             Will be thrown if attribute values were not set appropriately
     */
    private static void setFieldValue(Element field, Object fieldValue, FieldValueTypeValue fieldValueType)
            throws GenericArtifactParsingException {
        // TODO Carefully specify conversion for every single type
        if (fieldValueType == null) {
            throw new GenericArtifactParsingException(
                    "Non valid value for field-attribute " + field.attributeValue(FIELD_NAME) + " specified.");
        }
        switch (fieldValueType) {
        case BASE64STRING: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_BASE64STRING);
            break;
        }
        case BOOLEAN: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_BOOLEAN);
            break;
        }
        case DATE: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_DATE);
            break;
        }
        case DATETIME: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_DATETIME);
            break;
        }
        case DOUBLE: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_DOUBLE);
            break;
        }
        case HTMLSTRING: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_HTML_STRING);
            break;
        }
        case INTEGER: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_INTEGER);
            break;
        }
        case STRING: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_STRING);
            break;
        }
        case USER: {
            addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_USER);
            break;
        }
        // case LIST: {
        // addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_LIST);
        // break;
        // }
        // case MULTI_SELECT_LIST: {
        // addAttribute(field, FIELD_VALUE_TYPE,
        // FIELD_VALUE_TYPE_MULTI_SELECT_LIST);
        // break;
        // }
        // case UNKNOWN: {
        // addAttribute(field, FIELD_VALUE_TYPE, FIELD_VALUE_TYPE_UNKNOWN);
        // break;
        // }
        default: {
            throw new GenericArtifactParsingException(
                    "Non valid value for field-attribute " + FIELD_VALUE_TYPE + " specified.");
        }
        }
        if (fieldValue == null || (fieldValue != null && fieldValue.equals("0"))) {
            addAttribute(field, FIELD_VALUE_IS_NULL, FIELD_VALUE_IS_NULL_TRUE);
        } else {
            addAttribute(field, FIELD_VALUE_IS_NULL, FIELD_VALUE_IS_NULL_FALSE);
            if (fieldValue instanceof Date)
                synchronized (df) {
                    setValue(field, df.format((Date) fieldValue), false);
                }
            else if (fieldValue instanceof Calendar)
                synchronized (df) {
                    setValue(field, df.format(((Calendar) fieldValue).getTime()), false);
                }
            else if (fieldValue instanceof XMLGregorianCalendar)
                synchronized (df) {
                    setValue(field, df.format(((XMLGregorianCalendar) fieldValue).toGregorianCalendar().getTime()),
                            false);
                }
            else
                setValue(field, fieldValue.toString(), false);
        }
    }

    /**
     * Adds an attribute with the supplied value to the supplied element
     * 
     * @param element
     *            element in question
     * @param attributeName
     *            attribute name in question
     * @param value
     *            value of the attribute
     */
    private static void addAttribute(Element element, String attributeName, String value) {
        element.addAttribute(attributeName, value);
    }

    public static GenericArtifactField getMandatoryGAField(String name, GenericArtifact ga) {
        List<GenericArtifactField> gaFields = ga.getAllGenericArtifactFieldsWithSameFieldTypeAndFieldName(
                GenericArtifactField.VALUE_FIELD_TYPE_MANDATORY_FIELD, name);
        if (gaFields == null || gaFields.size() == 0) {
            return null;
        } else if (gaFields.size() == 1) {
            GenericArtifactField field = gaFields.get(0);
            return field;
        } else {
            throw new RuntimeException("More than one mandatory field with the same field name: " + name);
        }
    }

    public static GenericArtifactField getFlexGAField(String name, GenericArtifact ga) {
        List<GenericArtifactField> gaFields = ga.getAllGenericArtifactFieldsWithSameFieldTypeAndFieldName(
                GenericArtifactField.VALUE_FIELD_TYPE_FLEX_FIELD, name);
        if (gaFields == null || gaFields.size() == 0) {
            return null;
        } else if (gaFields.size() == 1) {
            GenericArtifactField field = gaFields.get(0);
            return field;
        } else {
            throw new RuntimeException("More than one flex field with the same field name: " + name);
        }
    }

    public static String getStringMandatoryGAField(String fieldName, GenericArtifact ga) {
        String fieldValue = null;
        GenericArtifactField gaField = getMandatoryGAField(fieldName, ga);
        if (gaField != null) {
            fieldValue = (String) gaField.getFieldValue();
        }
        return fieldValue;
    }

    public static String getStringFlexGAField(String fieldName, GenericArtifact ga) {
        String fieldValue = null;
        GenericArtifactField gaField = getFlexGAField(fieldName, ga);
        if (gaField != null) {
            fieldValue = (String) gaField.getFieldValue();
        }
        return fieldValue;
    }

    public static int getIntMandatoryGAField(String fieldName, GenericArtifact ga) {
        int fieldValue = 0;
        GenericArtifactField gaField = getMandatoryGAField(fieldName, ga);
        if (gaField != null) {
            Object fieldValueObj = gaField.getFieldValue();
            if (fieldValueObj instanceof String) {
                String fieldValueString = (String) fieldValueObj;
                fieldValue = Integer.parseInt(fieldValueString);
            } else if (fieldValueObj instanceof Integer) {
                fieldValue = ((Integer) fieldValueObj).intValue();
            }
        }
        return fieldValue;
    }

    public static Date getDateMandatoryGAField(String fieldName, GenericArtifact ga) {
        Date fieldValue = null;
        GenericArtifactField gaField = getMandatoryGAField(fieldName, ga);
        if (gaField != null) {
            Object fieldValueObj = gaField.getFieldValue();
            if (fieldValueObj instanceof String) {
                String fieldValueString = (String) fieldValueObj;
                fieldValue = DateUtil.parse(fieldValueString);
            } else if (fieldValueObj instanceof Date) {
                fieldValue = (Date) fieldValueObj;
            }
        }
        return fieldValue;
    }

    public static String getStringGAField(String fieldName, GenericArtifact ga) {
        String fieldValue = null;
        GenericArtifactField gaField = GenericArtifactHelper.getGAField(fieldName, ga);
        if (gaField != null) {
            fieldValue = (String) gaField.getFieldValue();
        }
        return fieldValue;
    }

    public static GenericArtifactField getGAField(String name, GenericArtifact ga) {
        List<GenericArtifactField> gaFields = ga.getAllGenericArtifactFieldsWithSameFieldName(name);
        if (gaFields == null || gaFields.size() == 0) {
            return null;
        } else if (gaFields.size() == 1) {
            GenericArtifactField field = gaFields.get(0);
            return field;
        } else {
            throw new RuntimeException("More than one flex field with the same field name: " + name);
        }
    }
}