KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > syndication > feed > impl > CloneableBean


1 /*
2  * Copyright 2004 Sun Microsystems, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17 package com.sun.syndication.feed.impl;
18
19 import java.beans.PropertyDescriptor JavaDoc;
20 import java.io.Serializable JavaDoc;
21 import java.lang.reflect.Array JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24 import java.util.*;
25
26 /**
27  * Provides deep <b>Bean</b> clonning support.
28  * <p>
29  * It works on all read/write properties, recursively. It support all primitive types, Strings, Collections,
30  * Cloneable objects and multi-dimensional arrays of any of them.
31  * <p>
32  * @author Alejandro Abdelnur
33  *
34  */

35 public class CloneableBean implements Serializable JavaDoc, Cloneable JavaDoc {
36
37     private static final Class JavaDoc[] NO_PARAMS_DEF = new Class JavaDoc[0];
38     private static final Object JavaDoc[] NO_PARAMS = new Object JavaDoc[0];
39
40     private Object JavaDoc _obj;
41     private Set _ignoreProperties;
42
43     /**
44      * Default constructor.
45      * <p>
46      * To be used by classes extending CloneableBean only.
47      * <p>
48      *
49      */

50     protected CloneableBean() {
51         _obj = this;
52     }
53
54     /**
55      * Creates a CloneableBean to be used in a delegation pattern.
56      * <p>
57      * For example:
58      * <p>
59      * <code>
60      * public class Foo implements Cloneable {
61      * private CloneableBean _cloneableBean;
62      *
63      * public Foo() {
64      * _cloneableBean = new CloneableBean(this);
65      * }
66      *
67      * public Object clone() throws CloneNotSupportedException {
68      * return _cloneableBean.beanClone();
69      * }
70      *
71      * }
72      * </code>
73      * <p>
74      * @param obj object bean to clone.
75      *
76      */

77     public CloneableBean(Object JavaDoc obj) {
78         this(obj,null);
79     }
80
81     /**
82      * Creates a CloneableBean to be used in a delegation pattern.
83      * <p>
84      * The property names in the ignoreProperties Set will not be copied into
85      * the cloned instance. This is useful for cases where the Bean has convenience
86      * properties (properties that are actually references to other properties or
87      * properties of properties). For example SyndFeed and SyndEntry beans have
88      * convenience properties, publishedDate, author, copyright and categories all
89      * of them mapped to properties in the DC Module.
90      * <p>
91      * @param obj object bean to clone.
92      * @param ignoreProperties properties to ignore when cloning.
93      *
94      */

95     public CloneableBean(Object JavaDoc obj,Set ignoreProperties) {
96         _obj = obj;
97         _ignoreProperties = (ignoreProperties!=null) ? ignoreProperties : Collections.EMPTY_SET;
98     }
99
100     /**
101      * Makes a deep bean clone of the object.
102      * <p>
103      * To be used by classes extending CloneableBean. Although it works also for classes using
104      * CloneableBean in a delegation pattern, for correctness those classes should use the
105      * @see #beanClone() beanClone method.
106      * <p>
107      * @return a clone of the object bean.
108      * @throws CloneNotSupportedException thrown if the object bean could not be cloned.
109      *
110      */

111     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
112         return beanClone();
113     }
114
115     /**
116      * Makes a deep bean clone of the object passed in the constructor.
117      * <p>
118      * To be used by classes using CloneableBean in a delegation pattern,
119      * @see #CloneableBean(Object) constructor.
120      *
121      * @return a clone of the object bean.
122      * @throws CloneNotSupportedException thrown if the object bean could not be cloned.
123      *
124      */

125     public Object JavaDoc beanClone() throws CloneNotSupportedException JavaDoc {
126         Object JavaDoc clonedBean;
127         try {
128             clonedBean = _obj.getClass().newInstance();
129             PropertyDescriptor JavaDoc[] pds = BeanIntrospector.getPropertyDescriptors(_obj.getClass());
130             if (pds!=null) {
131                 for (int i=0;i<pds.length;i++) {
132                     Method JavaDoc pReadMethod = pds[i].getReadMethod();
133                     Method JavaDoc pWriteMethod = pds[i].getWriteMethod();
134                     if (pReadMethod!=null && pWriteMethod!=null && // ensure it has getter and setter methods
135
!_ignoreProperties.contains(pds[i].getName()) && // is not in the list of properties to ignore
136
pReadMethod.getDeclaringClass()!=Object JavaDoc.class && // filter Object.class getter methods
137
pReadMethod.getParameterTypes().length==0) { // filter getter methods that take parameters
138
Object JavaDoc value = pReadMethod.invoke(_obj,NO_PARAMS);
139                         if (value!=null) {
140                             value = doClone(value);
141                             pWriteMethod.invoke(clonedBean,new Object JavaDoc[]{value});
142                         }
143                     }
144                 }
145             }
146         }
147         catch (CloneNotSupportedException JavaDoc cnsEx) {
148             throw cnsEx;
149         }
150         catch (Exception JavaDoc ex) {
151             System.out.println(ex);
152             ex.printStackTrace(System.out);
153             throw new CloneNotSupportedException JavaDoc("Cannot clone a "+_obj.getClass()+" object");
154         }
155         return clonedBean;
156     }
157
158     private Object JavaDoc doClone(Object JavaDoc value) throws Exception JavaDoc {
159         if (value!=null) {
160             Class JavaDoc vClass = value.getClass();
161             if (vClass.isArray()) {
162                 value = cloneArray(value);
163             }
164             else
165             if (value instanceof Collection) {
166                 value = cloneCollection((Collection)value);
167             }
168             else
169             if (value instanceof Map) {
170                 value = cloneMap((Map)value);
171             }
172             else
173             if (isBasicType(vClass)) {
174                 // NOTHING SPECIAL TO DO HERE, THEY ARE INMUTABLE
175
}
176             else
177             if (value instanceof Cloneable JavaDoc) {
178                 Method JavaDoc cloneMethod = vClass.getMethod("clone",NO_PARAMS_DEF);
179                 if (Modifier.isPublic(cloneMethod.getModifiers())) {
180                    value = cloneMethod.invoke(value,NO_PARAMS);
181                 }
182                 else {
183                     throw new CloneNotSupportedException JavaDoc("Cannot clone a "+value.getClass()+" object, clone() is not public");
184                 }
185             }
186             else {
187                 throw new CloneNotSupportedException JavaDoc("Cannot clone a "+vClass.getName()+" object");
188             }
189         }
190         return value;
191     }
192
193     private Object JavaDoc cloneArray(Object JavaDoc array) throws Exception JavaDoc {
194         Class JavaDoc elementClass = array.getClass().getComponentType();
195         int length = Array.getLength(array);
196         Object JavaDoc newArray = Array.newInstance(elementClass,length);
197         for (int i=0;i<length;i++) {
198             Object JavaDoc element = doClone(Array.get(array,i));
199             Array.set(newArray,i,element);
200         }
201         return newArray;
202     }
203
204     private Object JavaDoc cloneCollection(Collection collection) throws Exception JavaDoc {
205         Class JavaDoc mClass = collection.getClass();
206         Collection newColl = (Collection) mClass.newInstance();
207         Iterator i = collection.iterator();
208         while (i.hasNext()) {
209             Object JavaDoc element = doClone(i.next());
210             newColl.add(element);
211         }
212         return newColl;
213     }
214
215     private Object JavaDoc cloneMap(Map map) throws Exception JavaDoc {
216         Class JavaDoc mClass = map.getClass();
217         Map newMap = (Map) mClass.newInstance();
218         Iterator entries = map.entrySet().iterator();
219         while (entries.hasNext()) {
220             Map.Entry entry = (Map.Entry) entries.next();
221             Object JavaDoc key = doClone(entry.getKey());
222             Object JavaDoc value = doClone(entry.getValue());
223             newMap.put(key,value);
224         }
225         return newMap;
226     }
227
228     private static final Set BASIC_TYPES = new HashSet();
229
230     static {
231         BASIC_TYPES.add(Boolean JavaDoc.class);
232         BASIC_TYPES.add(Byte JavaDoc.class);
233         BASIC_TYPES.add(Character JavaDoc.class);
234         BASIC_TYPES.add(Double JavaDoc.class);
235         BASIC_TYPES.add(Float JavaDoc.class);
236         BASIC_TYPES.add(Integer JavaDoc.class);
237         BASIC_TYPES.add(Long JavaDoc.class);
238         BASIC_TYPES.add(Short JavaDoc.class);
239         BASIC_TYPES.add(String JavaDoc.class);
240     }
241
242     private static final Map CONSTRUCTOR_BASIC_TYPES = new HashMap();
243
244     static {
245         CONSTRUCTOR_BASIC_TYPES.put(Boolean JavaDoc.class,new Class JavaDoc[]{Boolean.TYPE});
246         CONSTRUCTOR_BASIC_TYPES.put(Byte JavaDoc.class,new Class JavaDoc[]{Byte.TYPE});
247         CONSTRUCTOR_BASIC_TYPES.put(Character JavaDoc.class,new Class JavaDoc[]{Character.TYPE});
248         CONSTRUCTOR_BASIC_TYPES.put(Double JavaDoc.class,new Class JavaDoc[]{Double.TYPE});
249         CONSTRUCTOR_BASIC_TYPES.put(Float JavaDoc.class,new Class JavaDoc[]{Float.TYPE});
250         CONSTRUCTOR_BASIC_TYPES.put(Integer JavaDoc.class,new Class JavaDoc[]{Integer.TYPE});
251         CONSTRUCTOR_BASIC_TYPES.put(Long JavaDoc.class,new Class JavaDoc[]{Long.TYPE});
252         CONSTRUCTOR_BASIC_TYPES.put(Short JavaDoc.class,new Class JavaDoc[]{Short.TYPE});
253         CONSTRUCTOR_BASIC_TYPES.put(String JavaDoc.class,new Class JavaDoc[]{String JavaDoc.class});
254     }
255
256     private boolean isBasicType(Class JavaDoc vClass) {
257         return BASIC_TYPES.contains(vClass);
258     }
259
260     // THIS IS NOT NEEDED, BASIC TYPES ARE INMUTABLE, TUCU 20040710
261
/**
262     private Object cloneBasicType(Object value) throws Exception {
263         Class pClass = value.getClass();
264         Class[] defType = (Class[]) CONSTRUCTOR_BASIC_TYPES.get(pClass);
265         Constructor cons = pClass.getDeclaredConstructor(defType);
266         value = cons.newInstance(new Object[]{value});
267         return value;
268     }
269     **/

270
271 }
272
273
Popular Tags