package org.exolab.castor.xml;

import java.util.Hashtable;
import java.util.Map;

/**
 * Default {@link IDResolver} for Castor XML during (un)marshaling.
 * 
 * @see org.exolab.castor.xml.IDResolver
 */
class IDResolverImpl implements IDResolver {

    /**
     * A collection of IDREF --> target object mappings.
     */
    private Map<String, Object> _idReferences = new Hashtable<String, Object>();

    /**
     * A custom (user-injected) IDResolver instance to be used for IDREF
     * resolution.
     */
    private IDResolver _idResolver = null;

    /**
     * Binds a mapping from an ID to the referenced target object.
     * 
     * @param id
     *            Object identifier
     * @param object
     *            Object being identified by ID
     * @param isValidating
     *            True if validation is enabled.
     * @throws ValidationException
     *             If an ID is used more than once.
     */
    void bind(final String id, final Object object, final boolean isValidating)
            throws ValidationException {

        if (isValidating && id == null) {
            throw new ValidationException("Invalid ID value 'null' encountered");
        }

        if (isValidating && id.equals("")) {
            throw new ValidationException("Empty ID value encountered");
        }

        if (isValidating && _idReferences.containsKey(id)) {
            if (!(id.equals("org.exolab.castor.mapping.MapItem") || id
                    .equals("HIGH-LOW"))) {
                throw new ValidationException("Duplicate ID " + id
                        + " encountered");
            }
        } else {
            _idReferences.put(id, object);
        }

    }

    /**
     * Returns the Object whose id matches the given IDREF, or 'null' if no
     * object was found.
     * 
     * @param idref
     *            the IDREF to resolve.
     * @return the Object whose id matches the given IDREF.
     */
    public Object resolve(final String idref) {

        Object object = _idReferences.get(idref);
        if (object != null) {
            return object;
        }

        if (_idResolver != null) {
            return _idResolver.resolve(idref);
        }

        return null;
    }

    /**
     * Sets a custom IDResolver instance to be used for IDRef resolution.
     * 
     * @param idResolver
     *            a custom IDResolver instance to be used.
     */
    void setResolver(final IDResolver idResolver) {
        _idResolver = idResolver;
    }

}
