/*******************************************************************************
 * Copyright (c) 2012, 2013 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 *
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *  - rbarkhouse - 24 February 2012 - 2.3.3 - Initial implementation
 ******************************************************************************/
package org.eclipse.persistence.internal.jaxb;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.xml.bind.ValidationEventHandler;

import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.jaxb.IDResolver;
import org.xml.sax.SAXException;

/**
 * INTERNAL:
 * <p>
 * This class provides an implementation of IDResolver that wraps
 * an IDResolver from the Sun JAXB Implementation.
 * </p>
 */
public class IDResolverWrapper extends IDResolver {

    private final static String BIND_METHOD_NAME = "bind";
    private final static String END_DOCUMENT_METHOD_NAME = "endDocument";
    private final static String RESOLVE_METHOD_NAME = "resolve";
    private final static String START_DOCUMENT_METHOD_NAME = "startDocument";
    
    private final static Class[] BIND_PARAMS = new Class[] { CoreClassConstants.STRING, CoreClassConstants.OBJECT };
    private final static Class[] RESOLVE_PARAMS = new Class[] { CoreClassConstants.STRING, CoreClassConstants.CLASS };
    private final static Class[] START_DOCUMENT_PARAMS = new Class[] { ValidationEventHandler.class };

    private Object resolver;
    private Method bindMethod, endDocumentMethod, resolveMethod, startDocumentMethod;
    
    public IDResolverWrapper(Object sunResolver) {
        this.resolver = sunResolver;
        Class resolverClass = sunResolver.getClass();

        try {
            this.bindMethod = PrivilegedAccessHelper.getMethod(resolverClass, BIND_METHOD_NAME, BIND_PARAMS, false);
        } catch (Exception ex) {
            throw XMLMarshalException.errorProcessingIDResolver(BIND_METHOD_NAME, sunResolver, ex);
        }
        try {
            this.endDocumentMethod = PrivilegedAccessHelper.getMethod(resolverClass, END_DOCUMENT_METHOD_NAME, new Class[] {}, false);
        } catch (Exception ex) {
            throw XMLMarshalException.errorProcessingIDResolver(END_DOCUMENT_METHOD_NAME, sunResolver, ex);
        }
        try {
            this.resolveMethod = PrivilegedAccessHelper.getMethod(resolverClass, RESOLVE_METHOD_NAME, RESOLVE_PARAMS, false);
        } catch (Exception ex) {
            throw XMLMarshalException.errorProcessingIDResolver(RESOLVE_METHOD_NAME, sunResolver, ex);
        }
        try {
            this.startDocumentMethod = PrivilegedAccessHelper.getMethod(resolverClass, START_DOCUMENT_METHOD_NAME, START_DOCUMENT_PARAMS, false);
        } catch (Exception ex) {
            throw XMLMarshalException.errorProcessingIDResolver(START_DOCUMENT_METHOD_NAME, sunResolver, ex);
        }
    }

    @Override
    public void bind(Object id, Object obj) throws SAXException {
        try {
            Object[] params = new Object[] { id.toString(), obj };
            PrivilegedAccessHelper.invokeMethod(this.bindMethod, this.resolver, params);
        } catch (Exception ex) {
            throw XMLMarshalException.errorInvokingIDResolver(BIND_METHOD_NAME, this.resolver, ex);
        }
    }
    
    @Override
    public void endDocument() throws SAXException {
        try {
            PrivilegedAccessHelper.invokeMethod(this.endDocumentMethod, this.resolver, new Object[] {});
        } catch (Exception ex) {
            throw XMLMarshalException.errorInvokingIDResolver(END_DOCUMENT_METHOD_NAME, this.resolver, ex);
        }
    }
    
    @Override
    public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
        try {
            Object[] params = new Object[] { eventHandler };
            PrivilegedAccessHelper.invokeMethod(this.startDocumentMethod, this.resolver, params);
        } catch (Exception ex) {
            throw XMLMarshalException.errorInvokingIDResolver(START_DOCUMENT_METHOD_NAME, this.resolver, ex);
        }
    }
    
    @Override
    public Callable<?> resolve(Object id, Class targetType) throws SAXException {
        try {
            Object[] params = new Object[] { id.toString(), targetType };
            return (Callable<?>) PrivilegedAccessHelper.invokeMethod(this.resolveMethod, this.resolver, params);
        } catch (Exception ex) {
            throw XMLMarshalException.errorInvokingIDResolver(RESOLVE_METHOD_NAME, this.resolver, ex);
        }
    }

    @Override
    public Callable<?> resolve(Map<String, Object> id, Class type) throws SAXException {
        // If the user is still using a Sun IDResolver, then they must only have one XML ID,
        // as only EclipseLink supports multiple IDs through XmlKey.  So if this method is called,
        // throw an exception informing the user.
        throw XMLMarshalException.wrappedIDResolverWithMultiID(id.toString(), this.resolver);
    }

    @Override
    public void bind(Map<String, Object> id, Object obj) throws SAXException {
        // If the user is still using a Sun IDResolver, then they must only have one XML ID,
        // as only EclipseLink supports multiple IDs through XmlKey.  So if this method is called,
        // throw an exception informing the user.
        throw XMLMarshalException.wrappedIDResolverWithMultiID(id.toString(), this.resolver);
    }

    public Object getResolver() {
        return this.resolver;
    }

}