001// Copyright 2004, 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.hivemind.util;
016
017import java.beans.PropertyEditor;
018import java.beans.PropertyEditorManager;
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Method;
021
022import org.apache.hivemind.ApplicationRuntimeException;
023
024/**
025 * Used to manage dynamic access to a property of a specific class.
026 * 
027 * @author Howard Lewis Ship
028 */
029public class PropertyAdaptor
030{
031    private String _propertyName;
032
033    private Class _propertyType;
034
035    private Method _readMethod;
036
037    private Method _writeMethod;
038
039    PropertyAdaptor(String propertyName, Class propertyType, Method readMethod, Method writeMethod)
040    {
041        _propertyName = propertyName;
042        _propertyType = propertyType;
043        _readMethod = readMethod;
044        _writeMethod = writeMethod;
045    }
046
047    /**
048     * Returns the name of the method used to read the property, or null if the property is not
049     * readable.
050     */
051    public String getReadMethodName()
052    {
053        return _readMethod == null ? null : _readMethod.getName();
054    }
055
056    /**
057     * Returns the name of the method used to write the property, or null if the property is not
058     * writable.
059     */
060    public String getWriteMethodName()
061    {
062        return _writeMethod == null ? null : _writeMethod.getName();
063    }
064
065    public String getPropertyName()
066    {
067        return _propertyName;
068    }
069
070    public Class getPropertyType()
071    {
072        return _propertyType;
073    }
074
075    /**
076     * Updates the property of the target object.
077     * 
078     * @param target
079     *            the object to update
080     * @param value
081     *            the value to be stored into the target object property
082     */
083    public void write(Object target, Object value)
084    {
085        if (_writeMethod == null)
086            throw new ApplicationRuntimeException(UtilMessages.noPropertyWriter(
087                    _propertyName,
088                    target), target, null, null);
089
090        try
091        {
092            _writeMethod.invoke(target, new Object[]
093            { value });
094
095        }
096        catch (Exception ex)
097        {
098            throw new ApplicationRuntimeException(UtilMessages.writeFailure(
099                    _propertyName,
100                    target,
101                    ex), target, null, ex);
102        }
103    }
104
105    public void smartWrite(Object target, String value)
106    {
107        Object convertedValue = convertValueForAssignment(target, value);
108
109        write(target, convertedValue);
110    }
111
112    /** @since 1.1 */
113    private Object convertValueForAssignment(Object target, String value)
114    {
115        if (value == null || _propertyType.isInstance(value))
116            return value;
117
118        PropertyEditor e = PropertyEditorManager.findEditor(_propertyType);
119
120        if (e == null)
121        {
122            Object convertedValue = instantiateViaStringConstructor(target, value);
123
124            if (convertedValue != null)
125                return convertedValue;
126
127            throw new ApplicationRuntimeException(UtilMessages.noPropertyEditor(
128                    _propertyName,
129                    target.getClass()));
130        }
131
132        try
133        {
134            e.setAsText(value);
135
136            return e.getValue();
137        }
138        catch (Exception ex)
139        {
140            throw new ApplicationRuntimeException(UtilMessages.unableToConvert(
141                    value,
142                    _propertyType,
143                    _propertyName,
144                    target,
145                    ex), null, ex);
146        }
147    }
148
149    /**
150     * Checks to see if this adaptor's property type has a public constructor that takes a single
151     * String argument.
152     */
153
154    private Object instantiateViaStringConstructor(Object target, String value)
155    {
156        try
157        {
158            Constructor c = _propertyType.getConstructor(new Class[]
159            { String.class });
160
161            return c.newInstance(new Object[]
162            { value });
163        }
164        catch (Exception ex)
165        {
166            return null;
167        }
168    }
169
170    /**
171     * Returns true if there's a write method for the property.
172     */
173    public boolean isWritable()
174    {
175        return _writeMethod != null;
176    }
177
178    /**
179     * Reads the property of the target object.
180     * 
181     * @param target
182     *            the object to read a property from
183     */
184    public Object read(Object target)
185    {
186        if (_readMethod == null)
187            throw new ApplicationRuntimeException(UtilMessages.noReader(_propertyName, target),
188                    target, null, null);
189
190        try
191        {
192            return _readMethod.invoke(target, null);
193
194        }
195        catch (Exception ex)
196        {
197            throw new ApplicationRuntimeException(UtilMessages.readFailure(
198                    _propertyName,
199                    target,
200                    ex), target, null, ex);
201        }
202    }
203
204    /**
205     * Returns true if there's a read method for the property.
206     */
207
208    public boolean isReadable()
209    {
210        return _readMethod != null;
211    }
212
213}