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