Package org.adfemg.datacontrol.xml

Source Code of org.adfemg.datacontrol.xml.DataControl

package org.adfemg.datacontrol.xml;


import java.util.LinkedHashMap;
import java.util.Map;

import oracle.adf.model.BindingContext;
import oracle.adf.model.adapter.AbstractImpl;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCInvokeMethod;
import oracle.adf.model.binding.DCUtil;
import oracle.adf.share.logging.ADFLogger;

import oracle.binding.AttributeContext;
import oracle.binding.DataFilter;
import oracle.binding.DefinitionProviderDataControl;
import oracle.binding.FilterableDataControl;
import oracle.binding.OperationBinding;
import oracle.binding.OperationInfo;
import oracle.binding.RowContext;
import oracle.binding.UpdateableDataControl;
import oracle.binding.criteria.SearchCriteria;
import oracle.binding.meta.Definition;
import oracle.binding.meta.StructureDefinition;

import org.adfemg.common.events.ListenerSupport;
import org.adfemg.datacontrol.xml.data.XMLDCAccessorTarget;
import org.adfemg.datacontrol.xml.data.XMLDCCollection;
import org.adfemg.datacontrol.xml.data.XMLDCElement;
import org.adfemg.datacontrol.xml.events.DataChangeEvent;
import org.adfemg.datacontrol.xml.events.DataChangeListener;
import org.adfemg.datacontrol.xml.events.ValidationListener;
import org.adfemg.datacontrol.xml.provider.data.DataProvider;
import org.adfemg.datacontrol.xml.provider.data.DataRequest;
import org.adfemg.datacontrol.xml.provider.data.DataRequestImpl;
import org.adfemg.datacontrol.xml.provider.data.EmptyElementProvider;
import org.adfemg.datacontrol.xml.provider.data.WSDataProvider;
import org.adfemg.datacontrol.xml.utils.Utils;

import org.w3c.dom.Element;


/**
* The runtime Data Control class.
* Inspired by oracle.adfinternal.model.adapter.url.xml.XMLDataControl
*
* @see AbstractImpl
* @see FilterableDataControl
* @see UpdateableDataControl
* @see DefinitionProviderDataControl
* @see oracle.adfinternal.model.adapter.url.xml.XMLDataControl
*/
public class DataControl extends AbstractImpl implements FilterableDataControl, UpdateableDataControl,
                                                         DefinitionProviderDataControl {
    private static final ADFLogger logger = ADFLogger.createADFLogger(DataControl.class);

    private final ListenerSupport<DataChangeListener> changeListeners =
        new ListenerSupport<DataChangeListener>(DataChangeListener.class);
    private final ListenerSupport<ValidationListener> validationListeners =
        new ListenerSupport<ValidationListener>(ValidationListener.class);

    /**
     * Default no-args constructor.
     */
    public DataControl() {
    }

    /**
     * Public constructur with DataControlDefinition.
     * Sets the definition through the AbstractImpl.
     * @param definition The DataControlDefinition to be set.
     */
    public DataControl(final DataControlDefinition definition) {
        setDefinition(definition);
    }

    /**
     * Gets the definition out of the AbstractImpl.
     *
     * @return the DataControlDefinition.
     */
    public DataControlDefinition getDCDefinition() {
        return (DataControlDefinition) getDefinition();
    }

    @Override
    public Definition getDefinition(final String name, final int type) {
        final DataControlDefinition def = getDCDefinition();
        if (def.usePersistedStructure()) {
            return null;
        }

        // We only provide StructureDefinitions.
        if (type != Definition.TYPE_STRUCTURE) {
            return null;
        }

        return def.findStructure(name);
    }

    /**
     * Invoke a method identified by the given action.
     *
     * @param map A Map of bindingContexts that provide access to all binding
     *            related objects.
     * @param action Operation on the datacontrol to be invoked.
     * @return <code>true</code> if this datacontrol has handled this action,
     *         <code>false</code> if the action should be interpreted in the
     *         bindings framework or in the caller.
     */
    @Override
    public boolean invokeOperation(final Map map, final OperationBinding action) {
        logger.finer("Invoke operation {0} from XML data control", action);

        // No operation to invoke, return false.
        if (action == null) {
            return false; // false means we did not handle this call
        }

        final OperationInfo operationInfo = action.getOperationInfo(); // is DCInvokeMethod
        if (!(operationInfo instanceof DCInvokeMethod)) {
            return false; // false means we did not handle this call
        }

        final BindingContext bindingContext = (BindingContext) map;
        final DCBindingContainer bindings = (DCBindingContainer) bindingContext.getCurrentBindingsEntry();
        final String methodName = operationInfo.getOperationName();

        // check for situation where the operation comes from a customization-class
        // annotation
        final Object instance = resolveAsExpression(bindingContext, bindings, operationInfo.getInstanceName());
        if (instance instanceof XMLDCElement) {
            XMLDCElement xmldce = (XMLDCElement) instance;
            if (xmldce.hasMethod(methodName, action.getParamsMap())) {
                // TODO: logging
                logger.fine("invoking @Operation method {0} on {1}, new Object[]{methodName, instance}");
                Object result = xmldce.invokeMethod(methodName, action.getParamsMap());
                logger.fine("returning {0}", result);
                processResult(result, bindingContext, action);
                return true; // true to indicate we handled the operation
            }
        }

        // see if invoked method is any one of ours
        DataControlDefinitionNode invokedDef = null;
        for (DataControlDefinitionNode defNode : getDCDefinition().getDefinitionNodes()) {
            if (defNode.getDatacontrolOperation().equals(methodName)) {
                logger.fine("dataControl {0} is handling the {0} datacontrol operation", new Object[] {
                            mName, methodName });
                invokedDef = defNode;
                break;
            }
        }
        if (invokedDef == null) {
            return false; // false means we did not handle this call
        }

        // determine StructureDefinition we should return
        final String beanClassName =
            getDCDefinition().getReturnStructName(getDCDefinition().getStructure(), invokedDef);
        final StructureDefinition structDef = getDCDefinition().findStructure(beanClassName);
        if (structDef == null) {
            throw new IllegalStateException("StructureDefinition " + beanClassName +
                                            " not found in the DataControl Definition: " + getDCDefinition().getName());
        }

        // build DataRequest (clone dynamicParams so we don't alter the ones from binding layer)
        Map<String, Object> mutableParamValues =
            new LinkedHashMap<String, Object>((Map<String, Object>) action.getParamsMap());
        DataRequest dataRequest = new DataRequestImpl(structDef, mutableParamValues, invokedDef);

        // get root XML element from dataProvider
        final DataProvider dataProvider = invokedDef.getProviderInstance(DataProvider.class);
        Element element = dataProvider.getRootElement(dataRequest);
        if (logger.isFine() && !(dataProvider instanceof WSDataProvider)) {
            // TODO: look at root data-provider not most outer provider(filter)
            // TODO: shouldn't each filter log the element at finest so we can see work of each filter
            // WSDataProvider takes care of its own logging
            logger.fine("data-provider {0} returned XML:\n{1}", new Object[] {
                        dataProvider.getClass().getName(), Utils.xmlNodeToString(Utils.toXMLNode(element)) });
        }

        // create XMLDCElement from XML element
        if (element == null) {
            // returning null would cause refresh issues once parameters change and the DC no
            // longer returns null
            element = new EmptyElementProvider().getRootElement(dataRequest);
        }
        final XMLDCElement xmldce = new XMLDCElement(this, structDef, element);

        // write XMLDCElement result to the binding layer
        processResult(xmldce, map, action);

        return true; // true means we handled this call
    }

    @Override
    public boolean invokeOperation(final Map bindingContext, final OperationBinding action, final DataFilter filter) {
        final SearchCriteria searchCriteria = filter.getSearchCriteria();
        if (searchCriteria != null && searchCriteria.getConjunction() != null &&
            searchCriteria.getSearchGroups() != null && !searchCriteria.getSearchGroups().isEmpty()) {
            throw new UnsupportedOperationException("DataFilter getSearchCriteria in invokeOperation not (yet) supported. ");
        }
        if (filter.getSearchAttributeNames() != null) {
            throw new UnsupportedOperationException("DataFilter getSearchAttributeNames in invokeOperation not (yet) supported. ");
        }
        if (filter.getSortCriteria() != null && filter.getSortCriteria().getSortItems() != null &&
            !filter.getSortCriteria().getSortItems().isEmpty()) {
            throw new UnsupportedOperationException("DataFilter getSortCriteria in invokeOperation not (yet) supported. ");
        }
        if (filter.getFetchStart() != 0) {
            throw new UnsupportedOperationException("DataFilter getFetchStart in invokeOperation not (yet) supported. ");
        }

        return invokeOperation(bindingContext, action);
    }

    @Override
    public XMLDCAccessorTarget invokeAccessor(final RowContext rowCtx, final String name, final DataFilter filter) {
        final Object rowDataProvider = rowCtx.getRowDataProvider();
        if (rowDataProvider instanceof XMLDCElement) {
            XMLDCElement elem = (XMLDCElement) rowDataProvider;
            if (!elem.containsAccessor(name)) {
                throw new IllegalArgumentException("XMLDCElement does not have an accessor named : " + name);
            }
            XMLDCAccessorTarget target = (XMLDCAccessorTarget) elem.get(name);
            return target;
        } else {
            throw new IllegalStateException("Unsupported rowDataProvider: " + rowDataProvider);
        }
    }

    /**
     * returns the name of the data control.
     */
    @Override
    public String getName() {
        return mName;
    }

    /**
     * Releases all references to the objects in the data provider layer.
     */
    @Override
    public void release() {
        logger.finer("DataControl.release NOOP");
    }

    /**
     * {@inheritDoc}
     *
     * @return {@inheritDoc}
     */
    @Override
    public Object getDataProvider() {
        return this;
    }

    ///////////////////////// Impl Updateable interface //////////////////////////

    /**
     * This method is called by the data binding framework when a new
     * value is to be set on an attribute in a bean. The attribute and bean
     * are provided in the attribute context along with other
     * framework context information.
     *
     * @param ctx the AttributeContext.
     * @param value the Object.
     * @return <code>true</code> if the base framework should skip any further
     *         processing of this attribute set. Otherwise return
     *         <code>false</code> so that framework can perform a set or put of
     *         the attribute value based on introspection.
     */
    @Override
    public boolean setAttributeValue(final AttributeContext ctx, final Object value) {
        return false;
    }

    /**
     * This method is called by the data binding framework when a new
     * row is needed from the data control.
     *
     * @param ctx Context of the new row.
     * @return A data object for the new row.
     */
    @Override
    public Object createRowData(final RowContext ctx) {
        if (logger.isFine()) {
            logger.fine("createRowData  master:{0}, accessor:{1}, index:{2}, nullCont:{3}, container:{4}, provider:{5}, type:{6}", new Object[] {
                        ctx.getMasterRowDataProvider(), ctx.getMasterAccessorName(), ctx.getCurrentRowIndex(),
                        ctx.isNullContainer(), ctx.getRowDataContainer(), ctx.getRowDataProvider(),
                        ctx.getRowDataProviderType()
            });
        }
        XMLDCElement master = (XMLDCElement) ctx.getMasterRowDataProvider();
        String accessorName = ctx.getMasterAccessorName();
        XMLDCAccessorTarget newChild;
        if (master.isCollection(accessorName)) {
            if (ctx.isNullContainer()) {
                throw new UnsupportedOperationException("DataControl.createRowData for collection with NullContainer not (yet) supported.");
            }
            XMLDCCollection coll = (XMLDCCollection) ctx.getRowDataContainer();
            newChild = coll.createElement(ctx.getCurrentRowIndex(), master);
        } else {
            Object existingChild = master.get(accessorName);
            if (existingChild instanceof XMLDCAccessorTarget) {
                logger.warning("Ignore the create for {0}.{1} because it already exists.", new Object[] {
                               master, accessorName });
                newChild = (XMLDCAccessorTarget) existingChild;
            } else {
                newChild = master.createChild(accessorName);
            }
        }
        return newChild;
    }

    /**
     * This method is called by the data binding facility before the row in the
     * RowContext object is modified or marked as removed, so the row can be
     * marked dirty by the data control.
     *
     * @param ctx Context of the row to be modified or removed.
     * @return The data object that the row represents.
     */
    @Override
    public Object registerDataProvider(final RowContext ctx) {
        changeListeners.broadcast().dataChanged(new DataChangeEvent(this, ctx));
        if (logger.isFine()) {
            logger.fine("registerDataProvider  master:{0}, accessor:{1}, index:{2}, nullCont:{3}, container:{4}, provider:{5}, type:{6}", new Object[] {
                        ctx.getMasterRowDataProvider(), ctx.getMasterAccessorName(), ctx.getCurrentRowIndex(),
                        ctx.isNullContainer(), ctx.getRowDataContainer(), ctx.getRowDataProvider(),
                        ctx.getRowDataProviderType()
            });
        }
        return ctx.getRowDataProvider();
    }

    /**
     * This method is called by the data binding facility when a row
     * should be removed from the underlying data source.
     *
     * @param ctx Context of the row to be removed.
     * @return <code>true</code> if the operation is sucessful, <code>false</code>
     *         otherwise.
     */
    @Override
    public boolean removeRowData(final RowContext ctx) {
        if (logger.isFine()) {
            logger.fine("removeRowData  master:{0}, accessor:{1}, index:{2}, nullCont:{3}, container:{4}, provider:{5}, type:{6}", new Object[] {
                        ctx.getMasterRowDataProvider(), ctx.getMasterAccessorName(), ctx.getCurrentRowIndex(),
                        ctx.isNullContainer(), ctx.getRowDataContainer(), ctx.getRowDataProvider(),
                        ctx.getRowDataProviderType()
            });
        }
        XMLDCElement master = (XMLDCElement) ctx.getMasterRowDataProvider();
        String accessorName = ctx.getMasterAccessorName();
        if (master.isCollection(accessorName)) {
            XMLDCCollection collection = (XMLDCCollection) master.get(accessorName);
            collection.remove(ctx.getCurrentRowIndex());
        } else {
            master.put(accessorName, null);
        }
        return true;
    }

    /**
     * Validates transaction if dirty.
     */
    @Override
    public void validate() {
        logger.finer("DataControl.validate calling ValidationListeners");
        // do not use broadcast as we want to remove listeners after invoking them
        // perhaps ListenerSupport should add an alternative to broadcase that
        // removes listeners after invoking them
        ValidationListener[] listeners = validationListeners.getListeners();
        for (ValidationListener listener : listeners) {
            listener.validate();
            // remove listener if no exception was thrown
            validationListeners.removeListener(listener);
        }
    }

    ///////////// LISTENER IMPL

    /**
     * Add a DataChangeListener to the DataControl.
     * @param listener the DataChangeListener to add.
     */
    public void addChangeListener(final DataChangeListener listener) {
        changeListeners.addListener(listener);
    }

    /**
     * Remove a DataChangeListener from the DataControl.
     * @param listener the DataChangeListener to remove.
     */
    public void removeChangeListener(final DataChangeListener listener) {
        changeListeners.removeListener(listener);
    }

    /**
     * Add a ValidationListener to the DataControl.
     * @param listener The ValidationListener to add.
     */
    public void addValidationListener(final ValidationListener listener) {
        // FIXME: prevent duplicate listeners here or make sure ListenerSupport
        // uses a Set and not a List
        validationListeners.addListener(listener);
    }

    /**
     * Remove a ValidationListener from the DataControl.
     * @param listener The Listener to remove.
     */
    public void removeValidationListener(final ValidationListener listener) {
        validationListeners.removeListener(listener);
    }

    @Override
    public String toString() {
        return new StringBuilder(getClass().getName()).append("[").append(getName()).append("]").toString();
    }

    // copied from package private oracle.adf.model.binding.DCInvokeMethod#resolveAsExpression
    static Object resolveAsExpression(Object bindingContext, Object rootObj, String expression) {
        if (expression == null || expression.isEmpty()) {
            return null;
        }
        final String EL_start = "${";
        final String EL_end = "}";

        if (!(expression.startsWith(EL_start) && expression.endsWith(EL_end))) {
            //convert by adding elexpression syntax
            expression = (new StringBuffer().append(EL_start).append(expression).append(EL_end)).toString();
        }
        return DCUtil.elEvaluate(bindingContext, rootObj, expression);
    }

}
TOP

Related Classes of org.adfemg.datacontrol.xml.DataControl

TOP
Copyright © 2015 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.