001: /*
002: * Koala Bean Markup Language - Copyright (C) 1999 Dyade
003: *
004: * Permission is hereby granted, free of charge, to any person obtaining a
005: * copy of this software and associated documentation files
006: * (the "Software"), to deal in the Software without restriction, including
007: * without limitation the rights to use, copy, modify, merge, publish,
008: * distribute, sublicense, and/or sell copies of the Software, and to permit
009: * persons to whom the Software is furnished to do so, subject to the
010: * following conditions:
011: * The above copyright notice and this permission notice shall be included
012: * in all copies or substantial portions of the Software.
013: *
014: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
015: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
016: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
017: * IN NO EVENT SHALL Dyade BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
019: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020: * DEALINGS IN THE SOFTWARE.
021: *
022: * Except as contained in this notice, the name of Dyade shall not be
023: * used in advertising or otherwise to promote the sale, use or other
024: * dealings in this Software without prior written authorization from
025: * Dyade.
026: *
027: * $Id: Util.java,v 1.6 2000/07/07 17:57:23 tkormann Exp $
028: * Author: Thierry.Kormann@sophia.inria.fr
029: */
030:
031: package fr.dyade.koala.xml.kbml;
032:
033: import java.io.*;
034: import java.util.Hashtable;
035: import java.beans.*;
036: import java.lang.reflect.*;
037: import fr.dyade.koala.xml.kbml.editors.*;
038:
039: /**
040: * A collection of utility methods for KBML.
041: *
042: * @author Philippe.Kaplan@sophia.inria.fr
043: * @author Thierry.Kormann@sophia.inria.fr
044: */
045: public class Util {
046:
047: /**
048: * Returns the property descriptor from the specified set of property
049: * descriptors and which has the specified property name.
050: * @param pd the property descriptor set
051: * @param prop the property name
052: */
053: public static PropertyDescriptor getPropertyDescriptor(
054: PropertyDescriptor[] pds, String prop) {
055: for (int i = 0; i < pds.length; i++) {
056: if (pds[i].getName().equals(prop)) {
057: return pds[i];
058: }
059: }
060: return null;
061: }
062:
063: /**
064: * Registers default PropertyEditors to the
065: * java.beans.PropertyEditorManager, so the bean introspector can
066: * use them. This is done automatically for
067: * <code>KBMLSerializer</code> and <code>KBMLDeserializer</code>.
068: * <BR>PropertyEditors will be provided for the following types:
069: * <br><br>
070: * <ul>
071: * <li>java.lang.Boolean</li>
072: * <li>java.lang.Byte</li>
073: * <li>java.lang.Short</li>
074: * <li>java.lang.Integer</li>
075: * <li>java.lang.Long</li>
076: * <li>java.lang.Float</li>
077: * <li>java.lang.Double</li>
078: * <li>java.lang.Character</li>
079: * <li>char</li>
080: * <li>java.awt.Point</li>
081: * <li>java.awt.Rectangle</li>
082: * <li>java.awt.Dimension</li>
083: * <li>java.awt.Insets</li>
084: * <li>java.net.URL</li>
085: * </ul>
086: */
087: public static void initializePropertyEditorManager() {
088: // use default property editor
089: PropertyEditorManager.registerEditor(Boolean.class,
090: PropertyEditorManager.findEditor(Boolean.TYPE)
091: .getClass());
092: PropertyEditorManager.registerEditor(Byte.class,
093: PropertyEditorManager.findEditor(Byte.TYPE).getClass());
094: PropertyEditorManager
095: .registerEditor(Short.class, PropertyEditorManager
096: .findEditor(Short.TYPE).getClass());
097: PropertyEditorManager.registerEditor(Integer.class,
098: PropertyEditorManager.findEditor(Integer.TYPE)
099: .getClass());
100: PropertyEditorManager.registerEditor(Long.class,
101: PropertyEditorManager.findEditor(Long.TYPE).getClass());
102: // custom property editor to support NaN, POSITIVE_INFINITY
103: // and NEGATIVE_INFINITY
104: PropertyEditorManager.registerEditor(Float.class,
105: FloatEditor.class);
106: PropertyEditorManager.registerEditor(Float.TYPE,
107: FloatEditor.class);
108: PropertyEditorManager.registerEditor(Double.class,
109: DoubleEditor.class);
110: PropertyEditorManager.registerEditor(Double.TYPE,
111: DoubleEditor.class);
112: // custom editors
113: PropertyEditorManager.registerEditor(Character.class,
114: CharEditor.class);
115: PropertyEditorManager.registerEditor(Character.TYPE,
116: CharEditor.class);
117: PropertyEditorManager.registerEditor(java.awt.Point.class,
118: PointEditor.class);
119: PropertyEditorManager.registerEditor(java.awt.Font.class,
120: FontEditor.class);
121: PropertyEditorManager.registerEditor(java.net.URL.class,
122: URLEditor.class);
123: PropertyEditorManager.registerEditor(java.awt.Insets.class,
124: InsetsEditor.class);
125: PropertyEditorManager.registerEditor(java.awt.Rectangle.class,
126: RectangleEditor.class);
127: PropertyEditorManager.registerEditor(java.awt.Dimension.class,
128: DimensionEditor.class);
129: }
130:
131: /**
132: * Returns an XML version of the string (with entities to quote
133: * special characters like <>&"').
134: * @param s the string to convert
135: */
136: public static String toXML(String s) {
137: if (s == null) {
138: return "";
139: }
140: int slength = s.length();
141: StringBuffer result = new StringBuffer(slength);
142: for (int i = 0; i < slength; i++) {
143: char c = s.charAt(i);
144: switch (c) {
145: case '<':
146: result.append("<");
147: break;
148: case '>':
149: result.append(">");
150: break;
151: case '&':
152: result.append("&");
153: break;
154: case '"':
155: result.append(""");
156: break;
157: case '\'':
158: result.append("'");
159: break;
160: default:
161: if ((c == '\n') || (c == '\t') || (c == '\r'))
162: result.append(c);
163: else if ((c < ' ') || (c > 127)) {
164: result.append("&#");
165: result.append(String.valueOf((int) c));
166: result.append(";");
167: } else {
168: result.append(c);
169: }
170: }
171: }
172: return result.toString();
173: }
174:
175: ///////////////////////////////////////////////////////
176: // main: various runtime utilities
177:
178: /**
179: * Invokes the different methods. use "--help" for exact syntax.
180: */
181: public static void main(String[] argv) {
182: switch (argv.length) {
183: case 1:
184: if (argv[0].startsWith("-v")) {
185: printVersion();
186: System.exit(0);
187: }
188: case 2:
189: if (argv[0].startsWith("-R")) {
190: report(argv[1], false);
191: System.exit(0);
192: }
193: if (argv[0].startsWith("-r")) {
194: report(argv[1], true);
195: System.exit(0);
196: }
197: if (argv[0].startsWith("-s")) {
198: serialize(argv[1]);
199: System.exit(0);
200: }
201: if (argv[0].startsWith("-d")) {
202: deserialize(argv[1], null);
203: System.exit(0);
204: }
205: if (argv[0].startsWith("-c")) {
206: convert(argv[1]);
207: System.exit(0);
208: }
209: case 3:
210: if (argv[0].startsWith("-d")) {
211: deserialize(argv[1], argv[2]);
212: System.exit(0);
213: }
214: }
215: usage();
216: }
217:
218: /**
219: * Prints KBML version.
220: */
221: private static void printVersion() {
222: System.out
223: .println("KBML version is: " + KBMLSerializer.VERSION);
224: }
225:
226: /**
227: * Serializes in XML an empty bean. Save default is toggled on.
228: * <p>
229: * Example: java * fr.dyade.koala.xml.kbml.Util -serialize
230: * java.awt.Frame
231: *
232: * @param className The bean class to be serialised.
233: */
234: public static void serialize(String className) {
235: try {
236: KBMLSerializer bxo = new KBMLSerializer(System.out);
237: bxo
238: .setSerializationOptions(KBMLSerializer.WRITE_DEFAULT_VALUES);
239: bxo.writeKBMLStartTag();
240: bxo.writeBean(Class.forName(className).newInstance());
241: bxo.writeKBMLEndTag();
242: bxo.flush();
243: bxo.close();
244:
245: } catch (Exception e) {
246: e.printStackTrace();
247: }
248: }
249:
250: /**
251: * Converts a java serialized bean to XML on standard output.
252: * <p>
253: * Example: java fr.dyade.koala.xml.kbml.Util -convert
254: * myBean.ser
255: *
256: * @param filename The filename of java serialized file.
257: */
258: public static void convert(String filename) {
259: try {
260:
261: FileInputStream ostream = new FileInputStream(filename);
262: ObjectInputStream p = new ObjectInputStream(ostream);
263: Object bean = p.readObject();
264: ostream.close();
265:
266: KBMLSerializer bxo = new KBMLSerializer(System.out);
267: bxo.writeKBMLStartTag();
268: bxo.writeBean(bean);
269: bxo.writeKBMLEndTag();
270: bxo.flush();
271: bxo.close();
272: } catch (Exception e) {
273: e.printStackTrace();
274: }
275: }
276:
277: /**
278: * Deserializes a bean from KBML file. Useful to test whether
279: * runtime deserialisation raises an error or not.
280: * <P>Example: java
281: * fr.dyade.koala.xml.kbml.Util -deserialize test.kbml
282: *
283: * @param filename The bean class to be serialised.
284: * @param output If non-null, java-serialize the bean to the
285: * output filename.
286: */
287: public static Object deserialize(String filename, String output) {
288: Object bean = null;
289: try {
290: KBMLDeserializer in = new KBMLDeserializer(filename
291: .equals("-") ? System.in : new FileInputStream(
292: filename));
293: bean = in.readBean();
294: System.out.println("Bean " + bean + " of class\n"
295: + bean.getClass().getName()
296: + " is deserialised from\n\"" + filename
297: + "\" without error.");
298: // serialize behind...
299: if (output != null) {
300: FileOutputStream ostream = new FileOutputStream(output);
301: ObjectOutputStream p = new ObjectOutputStream(ostream);
302: p.writeObject(bean);
303: p.flush();
304: ostream.close();
305: System.out.println("written " + output + ".");
306: }
307: } catch (Exception e) {
308: e.printStackTrace();
309: }
310: return bean;
311: }
312:
313: /**
314: * Finds and prints all properties of a bean by automatic
315: * introspection. Useful to test beans and BeanInfo classes.
316: * <p>Warning: discards property without both accessors.
317: * <p>
318: * Example: java fr.dyade.koala.xml.kbml.Util -report
319: * java.awt.Container
320: *
321: * @param className The bean class to be visited.
322: * @param doSort true: the property list must be sorted before printed.
323: */
324: public static void report(String className, boolean doSort) {
325: try {
326: Class target = Class.forName(className);
327: dump(target, doSort);
328: } catch (Exception e) {
329: e.printStackTrace();
330: }
331: }
332:
333: private static void dump(Class beanClass, boolean doSort)
334: throws IntrospectionException {
335: BeanInfo info = null;
336: info = Introspector.getBeanInfo(beanClass);
337: System.out.println("Reporting workable properties for "
338: + beanClass);
339: putln();
340: put("property name", 17);
341: put("type", 40);
342: put("defined in class", 22);
343: putln();
344: putln();
345: // properties
346: PropertyDescriptor unsorted[] = info.getPropertyDescriptors();
347: PropertyDescriptor pds[] = new PropertyDescriptor[unsorted.length];
348: // filter out bad prop
349: for (int i = 0; i < unsorted.length; i++) {
350: PropertyDescriptor pd = unsorted[i];
351: if (pd == null) {
352: unsorted[i] = null;
353: continue;
354: }
355: // discard bad patterns
356: if (pd.getReadMethod() == null) {
357: unsorted[i] = null;
358: }
359: if (pd instanceof IndexedPropertyDescriptor) {
360: IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
361: if (ipd.getIndexedWriteMethod() == null) {
362: unsorted[i] = null;
363: }
364: } else if (pd.getWriteMethod() == null) {
365: unsorted[i] = null;
366: }
367: }
368: int last = doSort ? sort(beanClass, unsorted, pds) : 0;
369: // add remaining pds
370: for (int i = 0; i < pds.length; i++) {
371: if (unsorted[i] != null) {
372: pds[last++] = unsorted[i];
373: }
374: }
375: // write all
376: for (int i = 0; i < pds.length; i++) {
377: PropertyDescriptor pd = pds[i];
378: if (pd == null) {
379: continue;
380: }
381: //dump
382: put(pd.getName(), 17);
383: put(" ");
384: put("" + pd.getPropertyType().getName(), 40);
385: put("" + pd.getReadMethod().getDeclaringClass().getName(),
386: 22);
387: putln();
388: }
389: }
390:
391: // sort in subclass order
392: private static int sort(Class local, PropertyDescriptor[] unsorted,
393: PropertyDescriptor[] pds) {
394: int index;
395: if (local == Object.class) {
396: index = 0;
397: } else {
398: index = sort(local.getSuperclass(), unsorted, pds);
399: }
400: for (int i = 0; i < pds.length; i++) {
401: if (unsorted[i] != null
402: && unsorted[i].getReadMethod().getDeclaringClass() == local) {
403: pds[index++] = unsorted[i];
404: unsorted[i] = null;
405: }
406: }
407: return index;
408: }
409:
410: private static void put(String s) {
411: System.out.print(s);
412: }
413:
414: private static void put(String s, int size) {
415: System.out.print(s);
416: for (int i = s.length(); i < size; i++) {
417: System.out.print(" ");
418: }
419: }
420:
421: private static void putln() {
422: System.out.println("");
423: }
424:
425: static private void usage() {
426: System.out
427: .println("Usage: java fr.dyade.koala.xml.kbml.Util -[rRsdv] [arg]");
428: System.out.println();
429: System.out
430: .println("-r(eport) <class-name> : reports property list for class-name.");
431: System.out
432: .println("-R(EPORT) <class-name> : same as above, without sorting the entries.");
433: System.out
434: .println("-c(onvert) <java-ser-file> : read a serialized bean and convert it to XML.");
435: System.out
436: .println("-s(erialise) <class-name> : writes in KBML a default bean for the given class.");
437: System.out
438: .println("-d(eserialise) <filename> [<output>]: reads a bean from KBML file.\nIf filename is \"-\", reads from standard input.\nIf <output> is specified, java-serialize the bean to this file.");
439: System.out.println("-v(ersion) : prints KBML version.");
440: System.exit(1);
441: }
442: }
|