001    /**
002     * Copyright 2005-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.web.bind;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
020    import org.kuali.rice.krad.uif.UifParameters;
021    import org.kuali.rice.krad.uif.view.View;
022    import org.kuali.rice.krad.uif.UifConstants.ViewType;
023    import org.kuali.rice.krad.uif.service.ViewService;
024    import org.kuali.rice.krad.util.KRADUtils;
025    import org.kuali.rice.krad.web.form.UifFormBase;
026    import org.springframework.core.convert.ConversionService;
027    import org.springframework.util.Assert;
028    import org.springframework.validation.AbstractPropertyBindingResult;
029    import org.springframework.web.bind.ServletRequestDataBinder;
030    
031    import javax.servlet.ServletRequest;
032    import javax.servlet.http.HttpServletRequest;
033    import java.util.Map;
034    
035    /**
036     * Override of ServletRequestDataBinder in order to hook in the UifBeanPropertyBindingResult
037     * which instantiates a custom BeanWrapperImpl.
038     *
039     * @author Kuali Rice Team (rice.collab@kuali.org)
040     */
041    public class UifServletRequestDataBinder extends ServletRequestDataBinder {
042        protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
043                UifServletRequestDataBinder.class);
044    
045        private UifBeanPropertyBindingResult bindingResult;
046        private ConversionService conversionService;
047    
048        public UifServletRequestDataBinder(Object target) {
049            super(target);
050            setBindingErrorProcessor(new UifBindingErrorProcessor());
051        }
052    
053        public UifServletRequestDataBinder(Object target, String name) {
054            super(target, name);
055            setBindingErrorProcessor(new UifBindingErrorProcessor());
056        }
057    
058        /**
059         * Allows for a custom binding result class.
060         *
061         * @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
062         */
063        @Override
064        public void initBeanPropertyAccess() {
065            Assert.state(this.bindingResult == null,
066                    "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
067            this.bindingResult = new UifBeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(),
068                    getAutoGrowCollectionLimit());
069            if (this.conversionService != null) {
070                this.bindingResult.initConversion(this.conversionService);
071            }
072        }
073    
074        /**
075         * Allows for the setting attributes to use to find the data dictionary data from Kuali
076         *
077         * @see org.springframework.validation.DataBinder#getInternalBindingResult()
078         */
079        @Override
080        protected AbstractPropertyBindingResult getInternalBindingResult() {
081            if (this.bindingResult == null) {
082                initBeanPropertyAccess();
083            }
084            return this.bindingResult;
085        }
086    
087        /**
088         * Disallows direct field access for Kuali
089         *
090         * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
091         */
092        @Override
093        public void initDirectFieldAccess() {
094            LOG.error("Direct Field access is not allowed in UifServletRequestDataBinder.");
095            throw new RuntimeException("Direct Field access is not allowed in Kuali");
096        }
097    
098        /**
099         * Performs data binding from servlet request parameters to the form, initializes view object, then calls
100         * {@link org.kuali.rice.krad.web.form.UifFormBase#postBind(javax.servlet.http.HttpServletRequest)}
101         *
102         * <p>
103         * The view is initialized by first looking for the {@code viewId} parameter in the request. If found, the view is
104         * retrieved based on this id. If the id is not present, then an attempt is made to find a view by type. In order
105         * to retrieve a view based on type, the view request parameter {@code viewTypeName} must be present. If all else
106         * fails and the viewId is populated on the form (could be populated from a previous request), this is used to
107         * retrieve the view.
108         * </p>
109         *
110         * <p>
111         * Note the view is not initialized for Ajax requests that perform partial page updates or dialog updates or no
112         * updates at all
113         * </p>
114         *
115         * @param request - HTTP Servlet Request instance
116         */
117        @Override
118        public void bind(ServletRequest request) {
119            super.bind(request);
120    
121            UifFormBase form = (UifFormBase) this.getTarget();
122    
123            // if doing a partial page update or ajax request with no updating, do not initialize view
124            if (!form.isUpdateComponentRequest() && !form.isUpdateNoneRequest() && !form.isUpdateDialogRequest()) {
125                View view = null;
126    
127                // attempt to retrieve a view by unique identifier first
128                String viewId = request.getParameter(UifParameters.VIEW_ID);
129                if (viewId != null) {
130                    view = getViewService().getViewById(viewId);
131                } else {
132                    // attempt to get view instance by type parameters
133                    String viewTypeName = request.getParameter(UifParameters.VIEW_TYPE_NAME);
134                    ViewType viewType = StringUtils.isBlank(viewTypeName) ? form.getViewTypeName() : ViewType.valueOf(
135                            viewTypeName);
136    
137                    if (viewType != null) {
138                        Map<String, String> parameterMap = KRADUtils.translateRequestParameterMap(
139                                request.getParameterMap());
140                        view = getViewService().getViewByType(viewType, parameterMap);
141                    }
142    
143                    // if view not found attempt to find one based on the cached form
144                    if (view == null) {
145                        view = getViewFromPreviousModel(form);
146    
147                        if (view != null) {
148                            LOG.warn("Obtained viewId from cached form, this may not be safe!");
149                        }
150                    }
151                }
152    
153                if (view != null) {
154                    form.setViewId(view.getId());
155                    form.setView(view);
156                } else {
157                    form.setViewId(null);
158                    form.setView(null);
159                }
160            }
161    
162            form.postBind((HttpServletRequest) request);
163        }
164    
165        protected View getViewFromPreviousModel(UifFormBase form) {
166            // maybe we have a view id from the session form
167            if (form.getViewId() != null) {
168                return getViewService().getViewById(form.getViewId());
169            }
170    
171            return null;
172        }
173    
174        public ViewService getViewService() {
175            return KRADServiceLocatorWeb.getViewService();
176        }
177    
178    }
179    
180