com.amalto.core.storage.StorageMetadataUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.amalto.core.storage.StorageMetadataUtils.java

Source

/*
 * Copyright (C) 2006-2016 Talend Inc. - www.talend.com
 * 
 * This source code is available under agreement available at
 * %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
 * 
 * You should have received a copy of the agreement along with this program; if not, write to Talend SA 9 rue Pages
 * 92150 Suresnes, France
 */

package com.amalto.core.storage;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;

import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.talend.mdm.commmon.metadata.ComplexTypeMetadata;
import org.talend.mdm.commmon.metadata.CompoundFieldMetadata;
import org.talend.mdm.commmon.metadata.ContainedComplexTypeMetadata;
import org.talend.mdm.commmon.metadata.ContainedTypeFieldMetadata;
import org.talend.mdm.commmon.metadata.DefaultMetadataVisitor;
import org.talend.mdm.commmon.metadata.EnumerationFieldMetadata;
import org.talend.mdm.commmon.metadata.FieldMetadata;
import org.talend.mdm.commmon.metadata.MetadataUtils;
import org.talend.mdm.commmon.metadata.ReferenceFieldMetadata;
import org.talend.mdm.commmon.metadata.SimpleTypeFieldMetadata;
import org.talend.mdm.commmon.metadata.TypeMetadata;
import org.talend.mdm.commmon.metadata.Types;

import com.amalto.core.query.user.DateConstant;
import com.amalto.core.query.user.DateTimeConstant;
import com.amalto.core.query.user.TimeConstant;
import com.amalto.core.storage.record.DataRecord;
import com.amalto.core.storage.record.metadata.UnsupportedDataRecordMetadata;

/**
 * Similar to {@link org.talend.mdm.commmon.metadata.MetadataUtils} but with utility methods for use of metadata
 * information in {@link com.amalto.core.storage.Storage} API.
 */
public class StorageMetadataUtils {

    private static final Logger LOGGER = Logger.getLogger(StorageMetadataUtils.class);

    private StorageMetadataUtils() {
    }

    /**
     * Similar to
     * {@link #path(org.talend.mdm.commmon.metadata.ComplexTypeMetadata, org.talend.mdm.commmon.metadata.FieldMetadata, boolean)}
     * but will remain in entity boundaries (won't follow FK to other MDM entities).
     *
     * @param origin Point of entry in the metadata graph.
     * @param target Field to look for as end of path.
     * @return A path <b>within</b> type <code>origin</code> to field <code>target</code>. Returns empty stack if no
     * path could be found.
     * @throws IllegalArgumentException If either <code>origin</code> or <code>path</code> is null.
     */
    public static List<FieldMetadata> path(ComplexTypeMetadata origin, FieldMetadata target) {
        return path(origin, target, true);
    }

    /**
     * <p>
     * Find <b>a</b> path (<b>not necessarily the shortest</b>) from type <code>origin</code> to field
     * <code>target</code>.
     * </p>
     * <p>
     * Method is expected to run in linear time, depending on:
     * <ul>
     * <li>Number of fields in <code>origin</code>.</li>
     * <li>Number of references fields accessible from <code>origin</code>.</li>
     * </ul>
     * </p>
     *
     * @param type Point of entry in the metadata graph.
     * @param target Field to look for as end of path.
     * @return A path from type <code>origin</code> to field <code>target</code>. Returns empty list if no path could be
     * found.
     * @throws IllegalArgumentException If either <code>origin</code> or <code>path</code> is null.
     */
    public static List<FieldMetadata> path(ComplexTypeMetadata type, FieldMetadata target,
            boolean includeReferences) {
        Stack<FieldMetadata> path = new Stack<FieldMetadata>();
        _path(type, target, path, new HashSet<ComplexTypeMetadata>(), includeReferences);
        return path;
    }

    private static void _path(ComplexTypeMetadata type, FieldMetadata target, Stack<FieldMetadata> path,
            Set<ComplexTypeMetadata> processedTypes, boolean includeReferences) {
        // Various optimizations for very simple cases
        if (type == null) {
            throw new IllegalArgumentException("Origin can not be null");
        }
        if (target == null) {
            throw new IllegalArgumentException("Target field can not be null");
        }
        if (Storage.PROJECTION_TYPE.equals(type.getName()) && type.hasField(target.getName())) {
            path.push(type.getField(target.getName()));
        }
        if (target.getContainingType() instanceof ContainedComplexTypeMetadata) {
            String targetPath = target.getPath();
            if (type.hasField(targetPath)) {
                StringTokenizer tokenizer = new StringTokenizer(targetPath, "/"); //$NON-NLS-1$
                StringBuilder currentPath = new StringBuilder();
                while (tokenizer.hasMoreTokens()) {
                    currentPath.append(tokenizer.nextToken()).append('/');
                    path.add(type.getField(currentPath.toString()));
                }
                return;
            }
        }
        //
        if (processedTypes.contains(type)) {
            return;
        }
        processedTypes.add(type);
        Collection<FieldMetadata> fields = type.getFields();
        for (FieldMetadata current : fields) {
            path.push(current);
            if (current.equals(target)) {
                return;
            }
            if (current instanceof ContainedTypeFieldMetadata) {
                ComplexTypeMetadata containedType = ((ContainedTypeFieldMetadata) current).getContainedType();
                _path(containedType, target, path, processedTypes, includeReferences);
                if (path.peek().equals(target)) {
                    return;
                }
                for (ComplexTypeMetadata subType : containedType.getSubTypes()) {
                    for (FieldMetadata field : subType.getFields()) {
                        if (field.getDeclaringType() == subType) {
                            _path(subType, target, path, processedTypes, includeReferences);
                            if (path.peek().equals(target)) {
                                return;
                            }
                        }
                    }
                }
            } else if (current instanceof ReferenceFieldMetadata) {
                if (includeReferences) {
                    ComplexTypeMetadata referencedType = ((ReferenceFieldMetadata) current).getReferencedType();
                    _path(referencedType, target, path, processedTypes, true);
                    if (path.peek().equals(target)) {
                        return;
                    }
                    for (ComplexTypeMetadata subType : referencedType.getSubTypes()) {
                        for (FieldMetadata field : subType.getFields()) {
                            if (field.getDeclaringType() == subType) {
                                _path(subType, target, path, processedTypes, true);
                                if (path.peek().equals(target)) {
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            path.pop();
        }
    }

    /**
     * <p>
     * Find <b>all</b> paths from type <code>origin</code> to field <code>target</code>.
     * </p>
     * <p>
     * This is a rather expensive operation, so use this method only when needed. When you need only <b>a</b> path to
     * field <code>target</code>, prefer usage of
     * {@link #path(org.talend.mdm.commmon.metadata.ComplexTypeMetadata, org.talend.mdm.commmon.metadata.FieldMetadata)}
     * .
     * </p>
     * <p>
     * This method follows references to other type <b>only</b> when type is not instantiable (see
     * {@link org.talend.mdm.commmon.metadata.TypeMetadata#isInstantiable()}).
     * </p>
     * 
     * @param type Point of entry in the metadata graph.
     * @param target Field to look for as end of path.
     * @return A path from type <code>origin</code> to field <code>target</code>. Returns empty list if no path could be
     * found.
     * @throws IllegalArgumentException If either <code>origin</code> or <code>path</code> is null.
     * @see #path(org.talend.mdm.commmon.metadata.ComplexTypeMetadata, org.talend.mdm.commmon.metadata.FieldMetadata)
     */
    public static Set<List<FieldMetadata>> paths(ComplexTypeMetadata type, FieldMetadata target) {
        Stack<FieldMetadata> path = new Stack<FieldMetadata>();
        HashSet<List<FieldMetadata>> foundPaths = new HashSet<List<FieldMetadata>>();
        _paths(type, target, path, foundPaths, new HashSet<TypeMetadata>());
        return foundPaths;
    }

    private static void _paths(ComplexTypeMetadata type, FieldMetadata target, Stack<FieldMetadata> currentPath,
            Set<List<FieldMetadata>> foundPaths, Set<TypeMetadata> processedTypes) {
        // Prevent infinite loop (in case of recursive relations)
        if (!processedTypes.add(type)) {
            return;
        }
        // Various optimizations for very simple cases
        if (type == null) {
            throw new IllegalArgumentException("Origin can not be null");
        }
        if (target == null) {
            throw new IllegalArgumentException("Target field can not be null");
        }
        if (target instanceof CompoundFieldMetadata) {
            FieldMetadata[] fields = ((CompoundFieldMetadata) target).getFields();
            for (FieldMetadata fieldMetadata : fields) {
                __paths(type, fieldMetadata, currentPath, foundPaths, processedTypes);
            }
        } else {
            __paths(type, target, currentPath, foundPaths, processedTypes);
        }
    }

    private static void __paths(ComplexTypeMetadata type, FieldMetadata target, Stack<FieldMetadata> currentPath,
            Set<List<FieldMetadata>> foundPaths, Set<TypeMetadata> processedTypes) {
        if (Storage.PROJECTION_TYPE.equals(type.getName()) && type.hasField(target.getName())) {
            currentPath.push(type.getField(target.getName()));
        }
        //
        Collection<FieldMetadata> fields = type.getFields();
        for (FieldMetadata current : fields) {
            currentPath.push(current);
            if (current.equals(target)) {
                foundPaths.add(new ArrayList<FieldMetadata>(currentPath));
            }
            if (current instanceof ContainedTypeFieldMetadata) {
                ComplexTypeMetadata containedType = ((ContainedTypeFieldMetadata) current).getContainedType();
                _paths(containedType, target, currentPath, foundPaths, processedTypes);
                for (ComplexTypeMetadata subType : containedType.getSubTypes()) {
                    for (FieldMetadata field : subType.getFields()) {
                        if (field.getDeclaringType().equals(subType)) {
                            _paths(subType, target, currentPath, foundPaths, processedTypes);
                        }
                    }
                }
            } else if (current instanceof ReferenceFieldMetadata) {
                ComplexTypeMetadata referencedType = ((ReferenceFieldMetadata) current).getReferencedType();
                if (!referencedType.isInstantiable()) {
                    if (processedTypes.contains(referencedType)) {
                        Collection<FieldMetadata> tempFields = referencedType.getFields();
                        for (FieldMetadata tempCurrent : tempFields) {
                            if (tempCurrent.equals(target)) {
                                currentPath.push(tempCurrent);
                                foundPaths.add(new ArrayList<FieldMetadata>(currentPath));
                                currentPath.pop();
                            }
                        }
                    }
                    _paths(referencedType, target, currentPath, foundPaths, processedTypes);
                    for (ComplexTypeMetadata subType : referencedType.getSubTypes()) {
                        for (FieldMetadata field : subType.getFields()) {
                            if (field.getDeclaringType() == subType) {
                                _paths(subType, target, currentPath, foundPaths, processedTypes);
                            }
                        }
                    }
                }
            }
            currentPath.pop();
        }
    }

    /**
     * Checks whether <code>value</code> is valid for <code>typeName</code>.
     *
     * @param value The value to check.
     * @param typeName The type name of the value (should be one of {@link org.talend.mdm.commmon.metadata.Types}).
     * @return <code>true</code> if correct, <code>false</code> otherwise.
     */
    public static boolean isValueAssignable(String value, String typeName) {
        try {
            convert(value, typeName);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * Checks whether <code>value</code> is valid for <code>typeName</code>.
     * 
     * @param value The value to check.
     * @param field The field to receive the value.
     * @return <code>true</code> if correct, <code>false</code> otherwise. Since all fields can receive
     * <code>null</code>, <code>null</code> always returns <code>true</code>.
     */
    public static boolean isValueAssignable(final String value, FieldMetadata field) {
        if (value == null) {
            return true;
        }
        try {
            List<TypeMetadata> fieldType = field.accept(new DefaultMetadataVisitor<List<TypeMetadata>>() {
                List<TypeMetadata> fieldTypes = new LinkedList<TypeMetadata>();

                @Override
                public List<TypeMetadata> visit(ReferenceFieldMetadata referenceField) {
                    fieldTypes
                            .add(MetadataUtils.getSuperConcreteType(referenceField.getReferencedField().getType()));
                    return fieldTypes;
                }

                @Override
                public List<TypeMetadata> visit(SimpleTypeFieldMetadata simpleField) {
                    fieldTypes.add(MetadataUtils.getSuperConcreteType(simpleField.getType()));
                    return fieldTypes;
                }

                @Override
                public List<TypeMetadata> visit(EnumerationFieldMetadata enumField) {
                    fieldTypes.add(MetadataUtils.getSuperConcreteType(enumField.getType()));
                    return fieldTypes;
                }
            });
            List<String> convertValue = field.accept(new DefaultMetadataVisitor<List<String>>() {
                List<String> values = new LinkedList<String>();

                @Override
                public List<String> visit(ReferenceFieldMetadata referenceField) {
                    if (value.startsWith("[")) { //$NON-NLS-1$
                        StringTokenizer tokenizer = new StringTokenizer(value, "["); //$NON-NLS-1$
                        while (tokenizer.hasMoreTokens()) {
                            String nextToken = tokenizer.nextToken();
                            values.add(nextToken.substring(1, nextToken.length() - 1));
                        }
                    } else {
                        values.add(value);
                    }
                    return values;
                }

                @Override
                public List<String> visit(SimpleTypeFieldMetadata simpleField) {
                    values.add(value);
                    return values;
                }

                @Override
                public List<String> visit(EnumerationFieldMetadata enumField) {
                    values.add(value);
                    return values;
                }
            });
            for (int i = 0; i < fieldType.size(); i++) {
                try {
                    convert(convertValue.get(i), fieldType.get(i));
                } catch (Exception e) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Checks whether <code>value</code> is valid for full text search.
     * 
     * @param value The value to check.
     * @param field The field to receive the value.
     * @return <code>true</code> if the field can be searched by lucene, <code>false</code> otherwise.
     */
    public static boolean isValueSearchable(final String value, FieldMetadata field) {
        // As we decided to index all user defined field with value String.valueOf(fieldValue)
        // All values should be searchable.
        // TODO: improve non-text field indexing by using the correct FieldBridge
        return true;
    }

    /**
     * Creates a value from <code>dataAsString</code>. Type and/or format of the returned value depends on
     * <code>field</code>. For instance, calling this method with {@link String} with value "0" and a field typed as
     * integer returns {@link Integer} instance with value 0.
     *
     * @param dataAsString A {@link String} containing content to initialize a value.
     * @param field A {@link FieldMetadata} that describes type information about the field.
     * @return A {@link Object} value that has correct type according to <code>field</code>. Returns <code>null</code>
     * if field is instance of {@link org.talend.mdm.commmon.metadata.ContainedTypeFieldMetadata} (this type of field
     * isn't expected to have values). Also returns <code>null</code> is parameter <code>dataAsString</code> is null
     * <b>OR</b> if <code>dataAsString</code> is empty string.
     * @throws RuntimeException Throws sub classes of {@link RuntimeException} if <code>dataAsString</code> format does
     * not match field's type.
     */
    public static Object convert(String dataAsString, FieldMetadata field) {
        return convert(dataAsString, field.getType());
    }

    public static Object convert(String dataAsString, FieldMetadata field, TypeMetadata actualType) {
        if (actualType == null) {
            // Use field's declared type if no actual type (TMDM-6898)
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Type is null, replacing type with field's declared type"); //$NON-NLS-1$
            }
            actualType = field.getType();
            if (actualType == null) {
                throw new IllegalArgumentException(
                        "Actual type for field '" + field.getName() + "' cannot be null."); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
        if (field instanceof ReferenceFieldMetadata) {
            if (dataAsString == null || dataAsString.trim().isEmpty()) {
                return null;
            }
            List<String> ids = getIds(dataAsString);
            if (ids.isEmpty()) {
                throw new IllegalArgumentException(
                        "Id '" + dataAsString + "' does not match expected format (no id found)."); //$NON-NLS-1$//$NON-NLS-2$
            }
            if (!(actualType instanceof ComplexTypeMetadata)) {
                throw new IllegalArgumentException(
                        "Type '" + actualType.getName() + "' was expected to be an entity type."); //$NON-NLS-1$//$NON-NLS-2$
            }
            ComplexTypeMetadata actualComplexType = (ComplexTypeMetadata) actualType;
            DataRecord referencedRecord = new DataRecord(actualComplexType, UnsupportedDataRecordMetadata.INSTANCE);
            Collection<FieldMetadata> keyFields = actualComplexType.getKeyFields();
            if (ids.size() != keyFields.size()) {
                throw new IllegalStateException("Type '" + actualType.getName() + "' expects " + keyFields.size() //$NON-NLS-1$//$NON-NLS-2$
                        + " keys values, but got " + ids.size() + "."); //$NON-NLS-1$//$NON-NLS-2$
            }
            Iterator<FieldMetadata> keyIterator = keyFields.iterator();
            for (String id : ids) {
                FieldMetadata nextKey = keyIterator.next();
                referencedRecord.set(nextKey, convert(id, nextKey));
            }
            return referencedRecord;
        } else {
            if (dataAsString == null) {
                return null;
            }
            if (dataAsString.trim().isEmpty()) { // Empty string is considered as null value
                return null;
            }
            TypeMetadata type = field.getType();
            if (!(field instanceof ContainedTypeFieldMetadata)) { // Contained (anonymous types) values can't have
                // values
                try {
                    return convert(dataAsString, type);
                } catch (Exception e) {
                    throw new RuntimeException("Could not convert value for field '" + field.getName() + "'", e);
                }
            } else {
                return null;
            }
        }
    }

    public static Object convert(String dataAsString, TypeMetadata type) {
        String typeName = type.getName();
        if (dataAsString == null || (dataAsString.isEmpty() && !Types.STRING.equals(typeName)
                && !typeName.contains("limitedString"))) { //$NON-NLS-1$
            return null;
        } else {
            TypeMetadata superType = org.talend.mdm.commmon.metadata.MetadataUtils.getSuperConcreteType(type);
            return convert(dataAsString, superType.getName());
        }
    }

    public static Object convert(String dataAsString, String type) {
        if (Types.STRING.equals(type) || Types.TOKEN.equals(type) || Types.DURATION.equals(type)) {
            return dataAsString;
        } else if (Types.INTEGER.equals(type) || Types.POSITIVE_INTEGER.equals(type)
                || Types.NEGATIVE_INTEGER.equals(type) || Types.NON_NEGATIVE_INTEGER.equals(type)
                || Types.NON_POSITIVE_INTEGER.equals(type) || Types.INT.equals(type)
                || Types.UNSIGNED_INT.equals(type)) {
            return Integer.parseInt(dataAsString);
        } else if (Types.DATE.equals(type)) {
            // Be careful here: DateFormat is not thread safe
            synchronized (DateConstant.DATE_FORMAT) {
                try {
                    DateFormat dateFormat = DateConstant.DATE_FORMAT;
                    Date date = dateFormat.parse(dataAsString);
                    return new Timestamp(date.getTime());
                } catch (Exception e) {
                    throw new RuntimeException("Could not parse date string", e);
                }
            }
        } else if (Types.DATETIME.equals(type)) {
            // Be careful here: DateFormat is not thread safe
            synchronized (DateTimeConstant.DATE_FORMAT) {
                try {
                    DateFormat dateFormat = DateTimeConstant.DATE_FORMAT;
                    Date date = dateFormat.parse(dataAsString);
                    return new Timestamp(date.getTime());
                } catch (Exception e) {
                    throw new RuntimeException("Could not parse date time string", e);
                }
            }
        } else if (Types.BOOLEAN.equals(type)) {
            // Boolean.parseBoolean returns "false" if content isn't a boolean string value. Callers of this method
            // expect call to fail if data is malformed.
            if ("0".equals(dataAsString)) { //$NON-NLS-1$
                return false;
            } else if ("1".equals(dataAsString)) { //$NON-NLS-1$
                return true;
            }
            if (!"false".equalsIgnoreCase(dataAsString) && !"true".equalsIgnoreCase(dataAsString)) { //$NON-NLS-1$ //$NON-NLS-2$
                throw new IllegalArgumentException("Value '" + dataAsString + "' is not valid for boolean");
            }
            return Boolean.parseBoolean(dataAsString);
        } else if (Types.DECIMAL.equals(type)) {
            try {
                return new BigDecimal(dataAsString);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("'" + dataAsString + "' is not a number.", e);
            }
        } else if (Types.FLOAT.equals(type)) {
            return Float.parseFloat(dataAsString);
        } else if (Types.LONG.equals(type) || Types.UNSIGNED_LONG.equals(type)) {
            return Long.parseLong(dataAsString);
        } else if (Types.ANY_URI.equals(type)) {
            return dataAsString;
        } else if (Types.SHORT.equals(type) || Types.UNSIGNED_SHORT.equals(type)) {
            return Short.parseShort(dataAsString);
        } else if (Types.QNAME.equals(type)) {
            return dataAsString;
        } else if (Types.BASE64_BINARY.equals(type)) {
            return dataAsString;
        } else if (Types.HEX_BINARY.equals(type)) {
            return dataAsString;
        } else if (Types.BYTE.equals(type) || Types.UNSIGNED_BYTE.equals(type)) {
            return Byte.parseByte(dataAsString);
        } else if (Types.DOUBLE.equals(type) || Types.UNSIGNED_DOUBLE.equals(type)) {
            return Double.parseDouble(dataAsString);
        } else if (Types.TIME.equals(type)) {
            // Be careful here: DateFormat is not thread safe
            synchronized (TimeConstant.TIME_FORMAT) {
                try {
                    DateFormat dateFormat = TimeConstant.TIME_FORMAT;
                    Date date = dateFormat.parse(dataAsString);
                    return new Timestamp(date.getTime());
                } catch (ParseException e) {
                    throw new RuntimeException("Could not parse time string", e);
                }
            }
        } else {
            throw new NotImplementedException("No support for type '" + type + "'");
        }
    }

    public static String toString(Object value) {
        return toString(value, true);
    }

    public static String toString(Object value, boolean escapeXml) {
        if (value == null) {
            return StringUtils.EMPTY;
        }
        if (value instanceof DataRecord) {
            DataRecord record = (DataRecord) value;
            StringBuilder builder = new StringBuilder();
            Collection<FieldMetadata> keyFields = record.getType().getKeyFields();
            for (FieldMetadata keyField : keyFields) {
                String keyFieldValue = StorageMetadataUtils.toString(record.get(keyField), keyField);
                TypeMetadata type = org.talend.mdm.commmon.metadata.MetadataUtils
                        .getSuperConcreteType(keyField.getType());
                if (Types.STRING.equals(type.getName()) && escapeXml) {
                    keyFieldValue = StringEscapeUtils.escapeXml(keyFieldValue);
                }
                builder.append('[').append(keyFieldValue).append(']');
            }
            return builder.toString();
        }
        if (escapeXml) {
            return StringEscapeUtils.escapeXml(String.valueOf(value));
        } else {
            return String.valueOf(value);
        }
    }

    public static String toString(Object o, FieldMetadata field) {
        if (o == null) {
            return StringUtils.EMPTY;
        }
        if (field instanceof ReferenceFieldMetadata) {
            return toString(o);
        }
        TypeMetadata type = MetadataUtils.getSuperConcreteType(field.getType());
        String typeName = type.getName();
        if (Types.DATE.equals(typeName)) {
            synchronized (DateConstant.DATE_FORMAT) {
                try {
                    DateFormat dateFormat = DateConstant.DATE_FORMAT;
                    return dateFormat.format(o);
                } catch (Exception e) {
                    throw new RuntimeException("Could not parse date '" + o + "'.", e);
                }
            }
        } else if (Types.DATETIME.equals(typeName)) {
            synchronized (DateTimeConstant.DATE_FORMAT) {
                try {
                    DateFormat dateFormat = DateTimeConstant.DATE_FORMAT;
                    return dateFormat.format(o);
                } catch (Exception e) {
                    throw new RuntimeException("Could not parse date time '" + o + "'.", e);
                }
            }
        } else if (Types.TIME.equals(typeName)) {
            synchronized (TimeConstant.TIME_FORMAT) {
                try {
                    DateFormat dateFormat = TimeConstant.TIME_FORMAT;
                    return dateFormat.format(o);
                } catch (Exception e) {
                    throw new RuntimeException("Could not parse time '" + o + "'.", e);
                }
            }
        } else {
            return String.valueOf(o);
        }
    }

    public static List<String> getIds(String dataAsString) {
        List<String> ids = new LinkedList<String>();
        if (dataAsString.startsWith("[")) { //$NON-NLS-1$
            char[] chars = dataAsString.toCharArray();
            StringBuilder builder = null;
            for (char currentChar : chars) {
                switch (currentChar) {
                case '[':
                    builder = new StringBuilder();
                    break;
                case ']':
                    if (builder != null) {
                        ids.add(builder.toString());
                    }
                    break;
                default:
                    if (builder != null) {
                        builder.append(currentChar);
                    }
                    break;
                }
            }
        } else {
            ids.add(dataAsString);
        }
        return ids;
    }
}