View Javadoc

1   /*
2    * Copyright (C) 2004 TiongHiang Lee This library is free software; you can
3    * redistribute it and/or modify it under the terms of the GNU Lesser General
4    * Public License as published by the Free Software Foundation; either version
5    * 2.1 of the License, or (at your option) any later version. This library is
6    * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7    * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
8    * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
9    * details. You should have received a copy of the GNU Lesser General Public
10   * License along with this library; if not, write to the Free Software
11   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
12   * Email: thlee@onemindsoft.org
13   */
14  
15  package org.onemind.commons.relabean.serialize;
16  
17  import java.beans.PropertyDescriptor;
18  import java.io.IOException;
19  import java.io.Writer;
20  import java.lang.reflect.Method;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import org.onemind.commons.java.util.StringUtils;
26  import org.onemind.commons.relabean.*;
27  /***
28   * A BeanModelWriter writes bean models, based on as BeanModelDescriptor, into
29   * XML. In addition, it can also write out the meta-model of the bean model.
30   * @author TiongHiang Lee (thlee@onemindsoft.org)
31   * @version $Id: BeanModelWriter.java,v 1.2 2004/08/26 16:29:00 thlee Exp $ $Name:  $
32   */
33  public class BeanModelWriter
34  {
35  
36      /*** the descriptor * */
37      private RelationalBeanModel _model;
38  
39      /*** the name binding * */
40      private NameBinding _binding;
41  
42      /*** object cache * */
43      private HashMap _objectCache = new HashMap();
44  
45      /***
46       * {@inheritDoc}
47       */
48      public BeanModelWriter(RelationalBeanModel model, NameBinding binding)
49      {
50          _model = model;
51          _binding = binding;
52          if (_binding == null)
53          {
54              _binding = new NameBinding();
55          }
56      }
57  
58      /***
59       * Write the meta model from starting from the root class
60       * @param w the writer
61       * @param modelRoot the model root class
62       * @throws IOException if there's IO problem
63       */
64      public void writeMetaModel(Writer w, Class modelRoot) throws IOException
65      {
66          writeMetaModel(w, Collections.singleton(modelRoot));
67      }
68  
69      /***
70       * Write multiple meta model from given model root classes
71       * @param w the writer
72       * @param modelRoots the model root classes
73       * @throws IOException if there's IO problem
74       */
75      public void writeMetaModel(Writer w, Collection modelRoots) throws IOException
76      {
77          writeModelHeader(w);
78          w.write("<meta-model name=\"" + _model.getDescriptor().getModelName() + "\">\n");
79          Iterator it = modelRoots.iterator();
80          while (it.hasNext())
81          {
82              Class rootClass = (Class) it.next();
83              RelationalBeanInfo info = _model.getBeanInfo(rootClass);
84              if (info == null)
85              {
86                  throw new IllegalArgumentException("Unknown bean class " + rootClass + " in model "
87                          + _model.getDescriptor().getModelName());
88              }
89              _writeMetaModelNode(w, "\t", info);
90          }
91          w.write("</meta-model>\n");
92      }
93  
94      /***
95       * Write a meta model node
96       * @param w the writer
97       * @param indent the indent
98       * @param info the bean info
99       * @throws IOException if there's IO error
100      */
101     private void _writeMetaModelNode(Writer w, String indent, RelationalBeanInfo info) throws IOException
102     {
103         String name = _binding.getName(info.getBeanClass());
104         if (name == null)
105         {
106             name = info.getBeanClass().getName();
107         }
108         w.write(indent + "<" + name + ">\n");
109         _writeMetaModelNodeProperties(w, indent + "\t", info);
110         w.write(indent + "</" + name + ">\n");
111     }
112 
113     /***
114      * Write the header
115      * @param w the writer
116      * @throws IOException if there's IO problem
117      */
118     private void writeModelHeader(Writer w) throws IOException
119     {
120         w.write("<?xml version=\"1.0\"?>\n");
121         w.write("<!-- written by BeanModelWriter -->\n");
122         w.write("<meta-info>\n");
123         _writeBinding(w);
124         _writePrimitives(w);
125         w.write("</meta-info>\n\n");
126     }
127 
128     /***
129      * Write the model
130      * @param writer the writer
131      * @param objects the objects
132      * @throws IOException if there's IO problem
133      */
134     public void writeModel(Writer writer, Collection objects) throws IOException
135     {
136         _objectCache.clear();
137         writeModelHeader(writer);
138         Iterator it = objects.iterator();
139         while (it.hasNext())
140         {
141             Object obj = (Object) it.next();
142             writeModelNode(writer, "", obj);
143         }
144     }
145 
146     /***
147      * Write the model node
148      * @param writer the writer
149      * @param indent the indent
150      * @param obj the object
151      * @throws IOException if there's IO problem
152      */
153     private void writeModelNode(Writer writer, String indent, Object obj) throws IOException
154     {
155         Integer id = (Integer) _objectCache.get(obj);
156         boolean written = false;
157         if (id == null)
158         { //not seen before
159             id = new Integer(_objectCache.size());
160             _objectCache.put(obj, id);
161         } else
162         {
163             written = true;
164         }
165         RelationalBeanInfo info = _model.getBeanInfo(obj.getClass());
166         if (info == null)
167         {
168             throw new IllegalArgumentException("Unknown object type " + obj + " in model " + _model.getDescriptor().getModelName());
169         }
170         String name = (String) _binding.getName(obj.getClass());
171         if (name == null)
172         {
173             name = obj.getClass().getName();
174         }
175         if (!written)
176         {
177             writer.write(indent + "<" + name + " _id_=\"" + id + "\">\n ");
178             writeModelNodeProperties(writer, indent + "\t", obj, info);
179             writer.write(indent + "</" + name + ">\n");
180         } else
181         {
182             writer.write(indent + "<" + name + " _refId_=\"" + id + "\"/>\n ");
183         }
184     }
185 
186     /***
187      * Writer the model node properties
188      * @param writer the writer
189      * @param indent the string
190      * @param obj the node object
191      * @param info the info
192      * @throws IOException if there's IO problem
193      */
194     private void writeModelNodeProperties(Writer writer, String indent, Object obj, RelationalBeanInfo info) throws IOException
195     {
196         Iterator it = info.getProperties().iterator();
197         while (it.hasNext())
198         {
199             PropertyDescriptor desc = (PropertyDescriptor) it.next();
200             Method m = desc.getReadMethod();
201             Object value;
202             try
203             {
204                 value = m.invoke(obj, new Object[]{});
205             } catch (Exception e)
206             {
207                 e.printStackTrace();
208                 throw new IOException("Cannot get property " + desc.getName() + " from object " + obj + ":" + e.getMessage());
209             }
210             if (value == null)
211             {
212                 writer.write(indent + "<property name=\"" + desc.getName() + "\" null=\"true\"/>\n");
213             } else
214             {
215                 writer.write(indent + "<property name=\"" + desc.getName() + "\" value=\"" + value + "\">\n");
216             }
217         }
218         it = info.getRelations().iterator();
219         while (it.hasNext())
220         {
221             RelationDescriptor desc = (RelationDescriptor) it.next();
222             Method m = desc.getReadMethod();
223             Object value;
224             try
225             {
226                 value = m.invoke(obj, new Object[]{});
227             } catch (Exception e)
228             {
229                 e.printStackTrace();
230                 throw new IOException("Cannot get property " + desc.getName() + " from object " + obj + ":" + e.getMessage());
231             }
232             String name = desc.getName();
233             if (value == null)
234             {
235                 writer.write(indent + "<" + name + " null=\"true\"/>\n");
236             } else
237             {
238                 writer.write(indent + "<" + name + ">\n");
239                 if (value != null)
240                 {
241                     if (value instanceof Collection)
242                     {
243                         Iterator objects = ((Collection) value).iterator();
244                         while (objects.hasNext())
245                         {
246                             Object object = objects.next();
247                             if (_model.getDescriptor().isPrimitiveType(object.getClass()))
248                             {
249                                 writePrimitive(writer, indent + "\t", object, info);
250                             } else
251                             {
252                                 writeModelNode(writer, indent + "\t", object);
253                             }
254                         }
255                     } else
256                     {
257                         if (_model.getDescriptor().isPrimitiveType(value.getClass()))
258                         {
259                             writePrimitive(writer, indent + "\t", value, info);
260                         } else
261                         {
262                             writeModelNode(writer, indent + "\t", value);
263                         }
264                     }
265                 }
266             }
267             writer.write(indent + "</" + name + ">\n");
268         }
269     }
270 
271     /***
272      * Writing primitive nodes
273      * @param writer the writer
274      * @param indent the indent
275      * @param object the object
276      * @param info the info
277      */
278     private void writePrimitive(Writer writer, String indent, Object object, RelationalBeanInfo info)
279     {
280         // TODO: TBI
281         //@todo TBI
282     }
283 
284     /***
285      * Resolve the name of class c using the binding setting
286      * @param c the class
287      * @return the name, or the class name if a binding is not found
288      */
289     private String _resolveName(Class c)
290     {
291         String name = _binding.getName(c);
292         if (name == null)
293         {
294             return c.getName();
295         } else
296         {
297             return name;
298         }
299     }
300 
301     /***
302      * Write the name binding
303      * @param w the writer
304      * @throws IOException if there's IO problem
305      */
306     private void _writeBinding(Writer w) throws IOException
307     {
308         w.write("\t<name-binding>\n");
309         Iterator it = _binding.getBindings().keySet().iterator();
310         while (it.hasNext())
311         {
312             Class clazz = (Class) it.next();
313             String name = (String) _binding.getName(clazz);
314             w.write("\t\t<binding class=\"" + clazz + "\" name=\"" + name + "\"//>\n");
315         }
316         w.write("\t</name-binding>\n");
317     }
318 
319     /***
320      * Write the primitives
321      * @param w the writer
322      * @throws IOException if there's IO problem
323      */
324     private void _writePrimitives(Writer w) throws IOException
325     {
326         w.write("\t<primitives>\n");
327         w.write("\t\t" + StringUtils.concat(_model.getDescriptor().getPrimitiveTypes(), ",\n\t\t") + "\n");
328         w.write("\t</primitives>\n");
329     }
330 
331     /***
332      * Writes out meta model node properties
333      * @param w the writer
334      * @param indent the indent
335      * @param info the bean info
336      * @throws IOException if there's IO problem
337      */
338     private void _writeMetaModelNodeProperties(Writer w, String indent, RelationalBeanInfo info) throws IOException
339     {
340         Iterator it = info.getProperties().iterator();
341         while (it.hasNext())
342         {
343             PropertyDescriptor desc = (PropertyDescriptor) it.next();
344             w.write(indent + "<field name=\"" + desc.getName() + "\" type=\"" + desc.getPropertyType().getName() + "\"//>\n");
345         }
346         it = info.getRelations().iterator();
347         while (it.hasNext())
348         {
349             RelationDescriptor desc = (RelationDescriptor) it.next();
350             w.write(indent + "<relation name=\"" + desc.getName() + "\" type=\"" + desc.getRelationType().getName() + "\"//>\n");
351         }
352     }
353 
354     /***
355      * Return the binding
356      * @return the binding
357      */
358     public NameBinding getBinding()
359     {
360         return _binding;
361     }
362 
363     /***
364      * Get the model
365      * @return the model
366      */
367     public RelationalBeanModel getModel()
368     {
369         return _model;
370     }
371 
372     /***
373      * Set the binding
374      * @param binding the binding
375      */
376     public void setBinding(NameBinding binding)
377     {
378         _binding = binding;
379     }
380 
381     /***
382      * Set the model
383      * @param model the model
384      */
385     public void setModel(RelationalBeanModel model)
386     {
387         _model = model;
388     }
389 }