001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krad.datadictionary.validation;
017
018import java.beans.PropertyDescriptor;
019import java.util.ArrayList;
020import java.util.List;
021
022import org.kuali.rice.krad.datadictionary.AttributeDefinition;
023import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
024import org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase;
025import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
026import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable;
027import org.springframework.beans.BeanWrapper;
028import org.springframework.beans.BeanWrapperImpl;
029import org.springframework.beans.InvalidPropertyException;
030
031/**
032 * This class allows a dictionary object to expose information about its fields / attributes, including the values of
033 * those fields, with some guidance from the DataDictionaryEntry object. 
034 * 
035 * @author Kuali Rice Team (rice.collab@kuali.org) 
036 */
037public class DictionaryObjectAttributeValueReader extends BaseAttributeValueReader {
038
039        protected Object object;
040        protected DataDictionaryEntry entry;
041
042        protected BeanWrapper beanWrapper;
043        
044        private String attributePath;
045        
046        public DictionaryObjectAttributeValueReader(Object object, String entryName, DataDictionaryEntry entry) {
047                this.object = object;
048                this.entry = entry;
049                this.entryName = entryName;
050
051                if (object != null){
052                        beanWrapper = new BeanWrapperImpl(object);
053                }
054        }
055        
056        public DictionaryObjectAttributeValueReader(Object object, String entryName, DataDictionaryEntry entry, String attributePath) {
057                this(object, entryName, entry);
058                this.attributePath = attributePath;
059        }
060        
061        @Override
062        public Constrainable getDefinition(String attrName) {
063                return entry != null ? entry.getAttributeDefinition(attrName) : null;
064        }
065        
066        @Override
067        public List<Constrainable> getDefinitions() {
068                if (entry instanceof DataDictionaryEntryBase) {
069                        DataDictionaryEntryBase entryBase = (DataDictionaryEntryBase)entry;
070                        List<Constrainable> definitions = new ArrayList<Constrainable>();
071                        List<AttributeDefinition> attributeDefinitions = entryBase.getAttributes();
072                        definitions.addAll(attributeDefinitions);
073                        return definitions;
074                }
075                
076                return null;
077        }
078        
079        @Override
080        public Constrainable getEntry() {
081                if (entry instanceof Constrainable)
082                        return (Constrainable) entry;
083                        
084                return null;
085        }
086        
087        @Override
088        public String getLabel(String attrName) {
089                AttributeDefinition attributeDefinition = entry != null ? entry.getAttributeDefinition(attrName) : null;
090                return attributeDefinition != null ? attributeDefinition.getLabel()  : attrName;
091        }
092
093        @Override
094        public Object getObject() {
095                return this.object;
096        }
097        
098        @Override
099        public String getPath() {
100                String path = ValidationUtils.buildPath(attributePath, attributeName);
101                return path != null ? path : "";
102        }
103
104        @Override
105        public Class<?> getType(String attrName) {
106                PropertyDescriptor propertyDescriptor = beanWrapper.getPropertyDescriptor(attrName);
107                
108                return propertyDescriptor.getPropertyType();
109        }
110
111    @Override
112    public boolean isReadable() {
113        return beanWrapper.isReadableProperty(attributeName);
114    }
115
116    @SuppressWarnings("unchecked")
117        @Override
118        public <X> X getValue() throws AttributeValidationException {
119                Object value = getValue(attributeName);
120                return (X) value;
121        }
122        
123        @SuppressWarnings("unchecked")
124        @Override
125        public <X> X getValue(String attrName) throws AttributeValidationException {
126                X attributeValue = null;
127                
128                Exception e = null;
129                try {
130                        attributeValue = (X) beanWrapper.getPropertyValue(attrName);
131                } catch (IllegalArgumentException iae) {
132                        e = iae;
133                } catch (InvalidPropertyException ipe){
134                        //just return null
135                }
136                
137                if (e != null)
138                        throw new AttributeValidationException("Unable to lookup attribute value by name (" + attrName + ") using introspection", e);
139                
140                
141                //                      JLR : KS has code to handle dynamic attributes -- not sure whether this is really needed anymore if we're actually relying on types
142                //            // Extract dynamic attributes
143                //            if(DYNAMIC_ATTRIBUTE.equals(propName)) {
144                //                dataMap.putAll((Map<String, String>)value);
145                //            } else {
146                //                              dataMap.put(propName, value);
147                //            }
148                
149                return attributeValue;
150        }
151        
152        /**
153         * @return false if parent attribute exists and is not null, otherwise returns true.
154         */
155        public boolean isParentAttributeNull(){
156            boolean isParentNull = true;
157            
158            if (isNestedAttribute()){
159                String[] pathTokens = attributeName.split("\\.");
160
161                isParentNull = false;
162            String parentPath = "";
163                for (int i=0; (i < pathTokens.length - 1) && !isParentNull;i++){
164                parentPath += pathTokens[i];
165                    isParentNull = beanWrapper.getPropertyValue(parentPath) == null;
166                    parentPath += ".";
167                }
168            }
169            
170            return isParentNull;
171        }
172        
173        public boolean isNestedAttribute(){
174            return (attributePath != null || attributeName.contains("."));
175        }
176
177    @Override
178        public AttributeValueReader clone(){
179            DictionaryObjectAttributeValueReader readerClone = 
180                new DictionaryObjectAttributeValueReader(this.object, this.entryName, this.entry, this.attributePath);
181            readerClone.setAttributeName(this.attributeName);
182            
183            
184            return readerClone;     
185        }
186        
187}