001 /* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * --------------------------
028 * BeanObjectDescription.java
029 * --------------------------
030 * (C)opyright 2003-2005, by Thomas Morgner and Contributors.
031 *
032 * Original Author: Thomas Morgner;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: BeanObjectDescription.java,v 1.6 2005/10/18 13:31:58 mungady Exp $
036 *
037 * Changes (from 19-Feb-2003)
038 * -------------------------
039 * 19-Feb-2003 : Added standard header and Javadocs (DG);
040 * 29-Apr-2003 : Distilled from the JFreeReport project and moved into
041 * JCommon (TM);
042 * 18-Aug-2005 : Added casts to suppress compiler warnings, as suggested in
043 * patch 1260622 (DG);
044 *
045 */
046
047 package org.jfree.xml.factory.objects;
048
049 import java.lang.reflect.Method;
050 import java.lang.reflect.Modifier;
051 import java.util.Iterator;
052 import java.util.TreeSet;
053 import java.util.HashMap;
054 import java.beans.Introspector;
055 import java.beans.IntrospectionException;
056 import java.beans.BeanInfo;
057 import java.beans.PropertyDescriptor;
058 import java.io.ObjectInputStream;
059 import java.io.IOException;
060
061 import org.jfree.util.Log;
062
063 /**
064 * An object-description for a bean object. This object description
065 * is very dangerous, if the bean contains properties with undefined
066 * types.
067 *
068 * @author Thomas Morgner
069 */
070 public class BeanObjectDescription extends AbstractObjectDescription {
071
072 private TreeSet ignoredParameters;
073 private transient HashMap properties;
074
075 /**
076 * Creates a new object description.
077 *
078 * @param className the class.
079 */
080 public BeanObjectDescription(final Class className) {
081 this(className, true);
082 }
083
084 /**
085 * Creates a new object description.
086 *
087 * @param className the class.
088 * @param init set to true, to autmaoticly initialise the object
089 * description. If set to false, the initialisation is
090 * elsewhere.
091 */
092 public BeanObjectDescription(final Class className, final boolean init) {
093 super(className);
094 // now create some method descriptions ..
095 this.ignoredParameters = new TreeSet();
096 readBeanDescription(className, init);
097 }
098
099 private boolean isValidMethod (final Method method, final int parCount)
100 {
101 if (method == null) {
102 return false;
103 }
104 if (!Modifier.isPublic(method.getModifiers())) {
105 return false;
106 }
107 if (Modifier.isStatic(method.getModifiers())) {
108 return false;
109 }
110 if (method.getParameterTypes().length != parCount) {
111 return false;
112 }
113 return true;
114 }
115
116 /**
117 * Creates an object based on this description.
118 *
119 * @return The object.
120 */
121 public Object createObject() {
122 try {
123 final Object o = getObjectClass().newInstance();
124 // now add the various parameters ...
125
126 final Iterator it = getParameterNames();
127 while (it.hasNext()) {
128 final String name = (String) it.next();
129
130 if (isParameterIgnored(name)) {
131 continue;
132 }
133
134 final Method method = findSetMethod(name);
135 final Object parameterValue = getParameter(name);
136 if (parameterValue == null) {
137 // Log.debug ("Parameter: " + name + " is null");
138 }
139 else {
140 method.invoke(o, new Object[]{parameterValue});
141 }
142 }
143 return o;
144 }
145 catch (Exception e) {
146 Log.error("Unable to invoke bean method", e);
147 }
148 return null;
149 }
150
151 /**
152 * Finds a set method in the bean.
153 *
154 * @param parameterName the parameter name.
155 *
156 * @return The method.
157 */
158 private Method findSetMethod(final String parameterName) {
159 final PropertyDescriptor descriptor
160 = (PropertyDescriptor) this.properties.get(parameterName);
161 return descriptor.getWriteMethod();
162 }
163
164 /**
165 * Finds a get method in the bean.
166 *
167 * @param parameterName the paramater name.
168 * @return The method.
169 */
170 private Method findGetMethod(final String parameterName) {
171 final PropertyDescriptor descriptor
172 = (PropertyDescriptor) this.properties.get(parameterName);
173 return descriptor.getReadMethod();
174 }
175
176 /**
177 * Sets the parameters in the description to match the supplied object.
178 *
179 * @param o the object (<code>null</code> not allowed).
180 *
181 * @throws ObjectFactoryException if there is a problem.
182 */
183 public void setParameterFromObject(final Object o)
184 throws ObjectFactoryException {
185 if (o == null) {
186 throw new NullPointerException("Given object is null");
187 }
188 final Class c = getObjectClass();
189 if (!c.isInstance(o)) {
190 throw new ObjectFactoryException("Object is no instance of " + c
191 + "(is " + o.getClass() + ")");
192 }
193
194 final Iterator it = getParameterNames();
195 while (it.hasNext()) {
196 final String propertyName = (String) it.next();
197
198 if (isParameterIgnored(propertyName)) {
199 continue;
200 }
201
202 try {
203 final Method method = findGetMethod(propertyName);
204 final Object retval = method.invoke(o, (Object[]) null);
205 if (retval != null) {
206 setParameter(propertyName, retval);
207 }
208 }
209 catch (Exception e) {
210 Log.info("Exception on method invokation.", e);
211 }
212
213 }
214 }
215
216 /**
217 * Adds a parameter to the ignored parameters.
218 *
219 * @param parameter the parameter.
220 */
221 protected void ignoreParameter(final String parameter) {
222 this.ignoredParameters.add (parameter);
223 }
224
225 /**
226 * Returns a flag that indicates whether or not the specified parameter is
227 * ignored.
228 *
229 * @param parameter the parameter.
230 *
231 * @return The flag.
232 */
233 protected boolean isParameterIgnored (final String parameter) {
234 return this.ignoredParameters.contains(parameter);
235 }
236
237 private void readObject(final ObjectInputStream in)
238 throws IOException, ClassNotFoundException {
239 in.defaultReadObject();
240 readBeanDescription(getObjectClass(), false);
241 }
242
243 private void readBeanDescription(final Class className, final boolean init) {
244 try {
245 this.properties = new HashMap();
246
247 final BeanInfo bi = Introspector.getBeanInfo(className);
248 final PropertyDescriptor[] propertyDescriptors
249 = bi.getPropertyDescriptors();
250 for (int i = 0; i < propertyDescriptors.length; i++)
251 {
252 final PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
253 final Method readMethod = propertyDescriptor.getReadMethod();
254 final Method writeMethod = propertyDescriptor.getWriteMethod();
255 if (isValidMethod(readMethod, 0) && isValidMethod(writeMethod, 1))
256 {
257 final String name = propertyDescriptor.getName();
258 this.properties.put(name, propertyDescriptor);
259 if (init) {
260 super.setParameterDefinition(name,
261 propertyDescriptor.getPropertyType());
262 }
263 }
264 }
265 }
266 catch (IntrospectionException e) {
267 Log.error ("Unable to build bean description", e);
268 }
269 }
270 }