001: /*
002: * Copyright (c) 2004 JETA Software, Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without modification,
005: * are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JETA Software nor the names of its contributors may
015: * be used to endorse or promote products derived from this software without
016: * specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
021: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
022: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
023: * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
024: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
025: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026: * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package com.jeta.forms.store.bean;
031:
032: import java.awt.Color;
033: import java.awt.Component;
034: import java.awt.Font;
035: import java.io.ObjectOutputStream;
036: import java.io.OutputStream;
037: import java.io.Serializable;
038: import java.util.Collection;
039: import java.util.HashMap;
040: import java.util.HashSet;
041: import java.util.Iterator;
042:
043: import javax.swing.LookAndFeel;
044:
045: import com.jeta.forms.gui.beans.JETABean;
046: import com.jeta.forms.gui.beans.JETABeanFactory;
047: import com.jeta.forms.gui.beans.JETAPropertyDescriptor;
048: import com.jeta.forms.gui.common.FormException;
049: import com.jeta.forms.logger.FormsLogger;
050: import com.jeta.forms.store.memento.PropertiesMemento;
051: import com.jeta.forms.store.properties.ColorHolder;
052: import com.jeta.forms.store.properties.FontProperty;
053: import com.jeta.forms.store.properties.JETAProperty;
054: import com.jeta.forms.store.properties.TransformOptionsProperty;
055:
056: /**
057: * This interface defines how a bean is instantiated and intialized from a
058: * stored state (i.e. a properties memento). See
059: * {@link com.jeta.forms.store.memento.PropertiesMemento}
060: *
061: * @author Jeff Tassin
062: */
063: public class DefaultBeanSerializer implements BeanSerializer {
064: /**
065: * A map of default beans classes to a instance of each class.
066: * m_default_beans<Class,Component>
067: */
068: private static HashMap m_default_beans = new HashMap();
069:
070: /**
071: * The current look and feel. We clear the default bean map when the look
072: * and feel changes.
073: */
074: private static LookAndFeel m_look_and_feel = null;
075:
076: /**
077: * A set of supported property types (Class objects) m_supported_properties<Class>
078: */
079: private static HashSet m_supported_properties = new HashSet();
080:
081: static {
082: /**
083: * load the supported properties map with the types of properties that
084: * can be stored (these are in addition to JETAProperty types)
085: */
086: m_supported_properties.add(java.awt.Color.class);
087: m_supported_properties.add(java.awt.Font.class);
088: m_supported_properties.add(String.class);
089: m_supported_properties.add(java.util.Calendar.class);
090: m_supported_properties.add(java.util.Date.class);
091: /** primitive types */
092: m_supported_properties.add(Boolean.class);
093: m_supported_properties.add(Byte.class);
094: m_supported_properties.add(Character.class);
095: m_supported_properties.add(Short.class);
096: m_supported_properties.add(Integer.class);
097: m_supported_properties.add(Long.class);
098: m_supported_properties.add(Float.class);
099: m_supported_properties.add(Double.class);
100: }
101:
102: /**
103: * Return true if the given two values are different.
104: */
105: private boolean areDifferent(Object def_value, Object prop_value) {
106: boolean bresult = true;
107: if (def_value != null) {
108: if (prop_value != null) {
109: bresult = !(def_value == prop_value
110: || def_value.equals(prop_value) || prop_value
111: .equals(def_value));
112: }
113: } else {
114: if (prop_value == null)
115: bresult = false;
116: else {
117: bresult = !(prop_value == def_value || prop_value
118: .equals(def_value));
119: }
120: }
121: return bresult;
122: }
123:
124: /**
125: * Returns a Java Bean instance that has only default properties. We use
126: * this to test which properties to save when serializing a JETABean. Null
127: * is returned if the component cannot be instantiated.
128: */
129: private JETABean getDefaultBean(Class compClass) {
130: if (isLookAndFeelChanged()) {
131: m_default_beans.clear();
132: }
133:
134: JETABean jbean = (JETABean) m_default_beans.get(compClass);
135: if (jbean == null) {
136: try {
137: jbean = JETABeanFactory.createBean(compClass.getName(),
138: null, true, false);
139: if (jbean == null) {
140: Component comp = (Component) compClass
141: .newInstance();
142: jbean = new JETABean(comp);
143: }
144: m_default_beans.put(compClass, jbean);
145: } catch (Exception e) {
146: FormsLogger.severe(e);
147: }
148: }
149: return jbean;
150: }
151:
152: /**
153: * Check if the look and feel has changed since the last time we called this
154: * method.
155: */
156: private static boolean isLookAndFeelChanged() {
157: LookAndFeel lf = javax.swing.UIManager.getLookAndFeel();
158: if (m_look_and_feel != lf) {
159: m_look_and_feel = lf;
160: return true;
161: }
162: return false;
163: }
164:
165: /**
166: * Returns true if the given value can be serialized. We don't allow storing
167: * some properties even though they implement Serializable. For example, we
168: * don't store properties that are instances of Containers and/or Components
169: * because those objects don't guarantee Serialiable compatibility with
170: * future versions of Java.
171: *
172: * @param value
173: * the value to check for serializability
174: * @return true if the given property value can be serialized.
175: */
176: protected boolean isPropertySerializable(Object value) {
177: if (value == null)
178: return true;
179:
180: Class c = value.getClass();
181: if (c.isPrimitive())
182: return true;
183:
184: if (value instanceof JETAProperty) {
185: JETAProperty jp = (JETAProperty) value;
186: if (jp instanceof TransformOptionsProperty)
187: return true;
188:
189: return !jp.isTransient();
190: }
191:
192: if (m_supported_properties.contains(c)) {
193: return true;
194: }
195:
196: if (value instanceof java.awt.Color
197: || value instanceof java.awt.Font
198: || value instanceof String
199: || value instanceof java.util.Calendar
200: || value instanceof java.util.Date)
201: return true;
202:
203: return false;
204: }
205:
206: /**
207: * Writes the bean state to a PropertiesMemento
208: */
209: public PropertiesMemento writeBean(JETABean jbean)
210: throws FormException {
211: try {
212: /** for invoking the read methods. Read methods don't take parameters */
213: Object[] read_params = new Object[0];
214: PropertiesMemento ppm = new PropertiesMemento();
215:
216: Component comp = jbean.getDelegate();
217: if (comp != null) {
218: ppm.setBeanClassName(comp.getClass().getName());
219: JETABean default_bean = getDefaultBean(comp.getClass());
220: if (default_bean != null) {
221: /**
222: * Iterate over all properties in the bean and store those
223: * properties that are read/write and are different from the
224: * default properties for a bean.
225: */
226: Collection prop_descriptors = jbean
227: .getPropertyDescriptors();
228: Iterator iter = prop_descriptors.iterator();
229: while (iter.hasNext()) {
230: JETAPropertyDescriptor jpd = (JETAPropertyDescriptor) iter
231: .next();
232: try {
233: Object def_value = jpd
234: .getPropertyValue(default_bean);
235: Object prop_value = jpd
236: .getPropertyValue(jbean);
237: /**
238: * only store a property if its value is different
239: * than the default value for the bean
240: */
241: if (areDifferent(def_value, prop_value)) {
242: if (isPropertySerializable(prop_value)
243: && (prop_value instanceof Serializable)) {
244: if (prop_value instanceof Font) {
245: /**
246: * need to do this because serialized
247: * fonts have problems on OS X
248: */
249: ppm
250: .addProperty(
251: jpd.getName(),
252: new FontProperty(
253: (Font) prop_value));
254: } else if (prop_value instanceof Color) {
255: /**
256: * Some Look and Feels use specialized
257: * Color objects that are invalid if the
258: * L&F is not present in the classpath.
259: * We don't form files to be dependent
260: * on any look and feel, so we need to
261: * store color holders instead of Color
262: * objects
263: */
264: ppm
265: .addProperty(
266: jpd.getName(),
267: new ColorHolder(
268: (Color) prop_value));
269: } else if (prop_value instanceof TransformOptionsProperty) {
270: /**
271: * special handling for transforms.
272: * store the actual value instead
273: */
274: ppm
275: .addProperty(
276: jpd.getName(),
277: new Integer(
278: ((TransformOptionsProperty) prop_value)
279: .getPropertyValue()));
280: } else {
281: ppm
282: .addProperty(
283: jpd.getName(),
284: (Serializable) prop_value);
285: }
286: }
287: }
288: } catch (Exception e) {
289: FormsLogger.severe(e);
290: }
291: }
292:
293: /**
294: * Always store the component name. I've encountered some
295: * Java Beans that don't define the 'name' property in the
296: * BeanInfo class. Since this architecture depends on the
297: * component name, we need to store it here regardless of
298: * whether it is declared in the bean info
299: */
300: try {
301: ppm.addProperty("name", comp.getName());
302: } catch (Exception e) {
303: FormsLogger.severe(e);
304: }
305: }
306: }
307: return ppm;
308: } catch (Exception e) {
309: if (e instanceof FormException)
310: throw (FormException) e;
311:
312: throw new FormException(e);
313: }
314: }
315:
316: /**
317: * Writes the bean state to the memento.
318: */
319: public void writeBean(OutputStream ostream, JETABean jbean)
320: throws FormException {
321: try {
322: ObjectOutputStream oos = new ObjectOutputStream(ostream);
323: oos.writeObject(writeBean(jbean));
324: } catch (Exception e) {
325: if (e instanceof FormException)
326: throw (FormException) e;
327:
328: throw new FormException(e);
329: }
330: }
331: }
|