001: /*
002: * PropertiesBean.java: a "serializable" Java Bean that uses a
003: * java.util.Properties backend.
004: * :noTabs=false:
005: *
006: * Copyright (C) 2006 Marcelo Vanzin
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Library General Public License as published
010: * by the Free Software Foundation; either version 2 of the License, or
011: * (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU Library General Public License for more details.
017: *
018: * You should have received a copy of the GNU Library General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: */
022: package org.gjt.sp.util;
023:
024: import java.beans.BeanInfo;
025: import java.beans.IntrospectionException;
026: import java.beans.Introspector;
027: import java.beans.PropertyDescriptor;
028:
029: import java.lang.reflect.Array;
030: import java.lang.reflect.Method;
031:
032: import java.util.Map;
033: import java.util.Properties;
034: import java.util.StringTokenizer;
035: import java.util.WeakHashMap;
036:
037: /**
038: * A "java bean" that can serialize itself into a java.util.Properties
039: * instance. For the serialization, the class uses the java beans
040: * instrospection mechanism to figure out the class's available
041: * properties, and saves all the properties as strings in the properties
042: * object.
043: *
044: * <p>Properties are saved based on a "root", which is set up during the
045: * instantiation of the object. The properties will be set as
046: * <code>root.property_name</code>.</p>
047: *
048: * <p>Only native types (boolean, char, double, float, int, long, short),
049: * Strings, and arrays of those types are supported. Also, nested
050: * beans are not supported presently.</p>
051: *
052: * @author Marcelo Vanzin
053: * @since jEdit 4.3pre7
054: */
055: public abstract class PropertiesBean {
056:
057: // Constructors
058:
059: /**
060: * Creates a new instance with the given root and the default array
061: * separator char (':').
062: *
063: * @param root A non-null string that will be the "root" of the
064: * serialized properties.
065: */
066: protected PropertiesBean(String root) {
067: this (root, ':');
068: }
069:
070: /**
071: * Creates a new instance with the given root and the given array
072: * separator character.
073: *
074: * @param root A non-null string that will be the "root" of the
075: * serialized properties.
076: * @param arraysep A character that will be used to define the
077: * separator of elements of an array property.
078: */
079: protected PropertiesBean(String root, char arraysep) {
080: if (root == null)
081: throw new IllegalArgumentException("root cannot be null");
082: this .root = root;
083: this .arraysep = arraysep;
084: }
085:
086: // Public methods
087:
088: /**
089: * Loads the bean's properties from the given object.
090: */
091: public void load(Properties p) {
092: try {
093: PropertyDescriptor[] _props = getPropertyDescriptors();
094: for (int i = 0; i < _props.length; i++) {
095: if ("class".equals(_props[i].getName()))
096: continue;
097:
098: Method _set = _props[i].getWriteMethod();
099: if (_set != null) {
100: String _pname = root + "." + _props[i].getName();
101: Object _val = p.getProperty(_pname);
102: if (_val != null)
103: _val = parse((String) _val, _props[i]
104: .getPropertyType());
105: _set.invoke(this , _val);
106: }
107: }
108: } catch (Exception e) {
109: // These exceptions shouldn't occur during normal runtime,
110: // so we catch them and print an error message. Users of this
111: // class should fix these before releasing the code.
112: Log.log(Log.ERROR, this , e);
113: }
114: }
115:
116: /**
117: * Saves the bean's properties into the given object.
118: */
119: public void save(Properties p) {
120: try {
121: PropertyDescriptor[] _props = getPropertyDescriptors();
122: for (int i = 0; i < _props.length; i++) {
123: if ("class".equals(_props[i].getName()))
124: continue;
125:
126: Method _get = _props[i].getReadMethod();
127: if (_get != null) {
128: Object _val = _get.invoke(this );
129: String _pname = root + "." + _props[i].getName();
130: if (_val != null)
131: p.setProperty(_pname, encode(_val));
132: else
133: p.remove(_pname);
134: }
135: }
136: } catch (Exception e) {
137: // These exceptions shouldn't occur during normal runtime,
138: // so we catch them and print an error message. Users of this
139: // class should fix these before releasing the code.
140: Log.log(Log.ERROR, this , e);
141: }
142: }
143:
144: /**
145: * Cleans the entries related to this object from the given object.
146: */
147: public void clean(Properties p) {
148:
149: try {
150: PropertyDescriptor[] _props = getPropertyDescriptors();
151: for (int i = 0; i < _props.length; i++) {
152: if ("class".equals(_props[i].getName()))
153: continue;
154:
155: String _pname = root + "." + _props[i].getName();
156: p.remove(_pname);
157: }
158: } catch (Exception e) {
159: // These exceptions shouldn't occur during normal runtime,
160: // so we catch them and print an error message. Users of this
161: // class should fix these before releasing the code.
162: Log.log(Log.ERROR, this , e);
163: }
164: }
165:
166: // Private methods
167:
168: private PropertyDescriptor[] getPropertyDescriptors()
169: throws IntrospectionException {
170: BeanInfo _info = Introspector.getBeanInfo(getClass());
171: return _info.getPropertyDescriptors();
172: }
173:
174: private String encode(Object value) {
175: Class _class = value.getClass();
176: if (_class.isArray()) {
177: StringBuilder _val = new StringBuilder();
178: int _len = Array.getLength(value);
179: for (int i = 0; i < _len; i++) {
180: String _str = encode(Array.get(value, i));
181: if (_str == null)
182: return null;
183: _val.append(_str);
184: if (i < _len - 1)
185: _val.append(arraysep);
186: }
187: return _val.toString();
188: } else {
189: // just make sure it's a supported type.
190: if (_class != Boolean.class && _class != Boolean.TYPE
191: && _class != Character.class
192: && _class != Character.TYPE
193: && _class != Double.class && _class != Double.TYPE
194: && _class != Float.class && _class != Float.TYPE
195: && _class != Integer.class
196: && _class != Integer.TYPE && _class != Long.class
197: && _class != Long.TYPE && _class != Short.class
198: && _class != Short.TYPE && _class != String.class) {
199: Log.log(Log.WARNING, this , "unsupported type: "
200: + _class.getName());
201: return null;
202: }
203: return value.toString();
204: }
205: }
206:
207: private Object parse(String value, Class<?> _class) {
208: Object _ret = null;
209: if (_class.isArray()) {
210: StringTokenizer st = new StringTokenizer(value, String
211: .valueOf(arraysep));
212: Class _type = _class.getComponentType();
213: _ret = Array.newInstance(_type, st.countTokens());
214: int _cnt = st.countTokens();
215: for (int i = 0; i < _cnt; i++) {
216: Object _val = parse(st.nextToken(), _type);
217: if (_val == null)
218: return null;
219: Array.set(_ret, i, _val);
220: }
221: } else {
222: if (_class == Boolean.class || _class == Boolean.TYPE)
223: _ret = Boolean.valueOf(value);
224: else if (_class == Character.class
225: || _class == Character.TYPE)
226: _ret = Character.valueOf(value.charAt(0));
227: else if (_class == Double.class || _class == Double.TYPE)
228: _ret = Double.valueOf(value);
229: else if (_class == Float.class || _class == Float.TYPE)
230: _ret = Float.valueOf(value);
231: else if (_class == Integer.class || _class == Integer.TYPE)
232: _ret = Integer.valueOf(value);
233: else if (_class == Long.class || _class == Long.TYPE)
234: _ret = Long.valueOf(value);
235: else if (_class == Short.class || _class == Short.TYPE)
236: _ret = Short.valueOf(value);
237: else if (_class == String.class)
238: _ret = value;
239: else
240: Log.log(Log.WARNING, this , "unsupported type: "
241: + _class.getName());
242:
243: }
244: return _ret;
245: }
246:
247: // Instance variables
248:
249: private final char arraysep;
250: private final String root;
251:
252: }
|