KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > xb > binding > MappingObjectModelFactory


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.xb.binding;
23
24 import org.jboss.logging.Logger;
25 import org.jboss.util.NestedRuntimeException;
26 import org.jboss.xb.binding.introspection.FieldInfo;
27 import org.xml.sax.Attributes JavaDoc;
28 import org.apache.xerces.xs.XSTypeDefinition;
29
30 import java.lang.reflect.Constructor JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Collection JavaDoc;
34 import java.util.ArrayList JavaDoc;
35
36 /**
37  * An ObjectModelFactory that uses mappings
38  *
39  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
40  * @author <a HREF="mailto:adrian@jboss.com">Adrian Brock</a>
41  * @version <tt>$Revision: 1958 $</tt>
42  */

43 public class MappingObjectModelFactory
44    implements GenericObjectModelFactory
45 {
46    private final static Logger log = Logger.getLogger(MappingObjectModelFactory.class);
47
48    /**
49     * The class mappings
50     */

51    private final Map JavaDoc elementToClassMapping = new HashMap JavaDoc();
52
53    /**
54     * The field mappings
55     */

56    private final Map JavaDoc elementToFieldMapping = new HashMap JavaDoc();
57
58    // Public
59

60    /**
61     * Map an element to a class
62     *
63     * @param element the element name
64     * @param cls the class
65     */

66    public void mapElementToClass(String JavaDoc element, Class JavaDoc cls)
67    {
68       ElementToClassMapping mapping = new ElementToClassMapping(element, cls);
69       addElementToClassMapping(mapping);
70       if(log.isTraceEnabled())
71       {
72          log.trace(mapping);
73       }
74    }
75
76    /**
77     * Map an element to a field
78     *
79     * @param element the element name
80     * @param cls the class
81     * @param field the field name
82     * @param converter the type convertor
83     */

84    public void mapElementToField(String JavaDoc element, Class JavaDoc cls, String JavaDoc field, TypeBinding converter)
85    {
86       ElementToFieldMapping mapping = new ElementToFieldMapping(element, cls, field, converter);
87       addElementToFieldMapping(mapping);
88       if(log.isTraceEnabled())
89       {
90          log.trace(mapping);
91       }
92    }
93
94    // ObjectModelFactory implementation
95

96    public Object JavaDoc newRoot(Object JavaDoc root,
97                          UnmarshallingContext ctx,
98                          String JavaDoc namespaceURI,
99                          String JavaDoc localName,
100                          Attributes JavaDoc attrs)
101    {
102       if(log.isTraceEnabled())
103       {
104          log.trace("newRoot root=" +
105             root +
106             " namespaceURI=" +
107             namespaceURI +
108             " localName=" +
109             localName +
110             " attributes=" +
111             attrs
112          );
113       }
114
115       if(root == null)
116       {
117          ElementToClassMapping mapping = (ElementToClassMapping)elementToClassMapping.get(localName);
118          if(mapping != null)
119          {
120             if(log.isTraceEnabled())
121             {
122                log.trace("creating root using " + mapping);
123             }
124             root = newInstance(mapping.cls);
125          }
126          else
127          {
128             root = create(namespaceURI, localName, ctx.getType());
129          }
130
131          if(root == null)
132          {
133             throw new IllegalStateException JavaDoc(
134                "Failed to resolve Java type binding for root element: ns=" + namespaceURI + ", local=" + localName
135             );
136          }
137       }
138
139       if(attrs != null)
140       {
141          for(int i = 0; i < attrs.getLength(); ++i)
142          {
143             try
144             {
145                if(attrs.getLocalName(i).length() > 0)
146                {
147                   if(!attrs.getQName(i).startsWith("xsi:")) //todo horrible
148
{
149                      setAttribute(root, attrs.getLocalName(i), attrs.getValue(i), ctx);
150                   }
151                }
152             }
153             catch(Exception JavaDoc e)
154             {
155                String JavaDoc msg = "Failed to set attribute " + attrs.getQName(i) + "=" + attrs.getValue(i);
156                log.error(msg, e);
157                throw new IllegalStateException JavaDoc(msg + ": " + e.getMessage());
158             }
159          }
160       }
161
162       return root;
163    }
164
165    // GenericObjectModelFactory implementation
166

167    public Object JavaDoc newChild(Object JavaDoc o,
168                           UnmarshallingContext ctx,
169                           String JavaDoc namespaceURI,
170                           String JavaDoc localName,
171                           Attributes JavaDoc attrs)
172    {
173       if(log.isTraceEnabled())
174       {
175          log.trace("newChild object=" +
176             o +
177             " namespaceURI=" +
178             namespaceURI +
179             " localName=" +
180             localName +
181             " attributes=" +
182             attrs
183          );
184       }
185
186       if(o == null)
187       {
188          throw new RuntimeException JavaDoc("Attempt to add a new child to a null parent localName=" + localName);
189       }
190
191       Object JavaDoc child = null;
192
193       ElementToClassMapping mapping = (ElementToClassMapping)elementToClassMapping.get(localName);
194       XSTypeDefinition type = ctx.getType();
195       if(mapping != null)
196       {
197          if(log.isTraceEnabled())
198          {
199             log.trace("newChild using mapping " + mapping);
200          }
201
202          try
203          {
204             if(!(o instanceof Collection JavaDoc))
205             {
206                ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get(
207                   new ElementToFieldMappingKey(localName, o.getClass())
208                );
209
210                FieldInfo fieldInfo;
211                if(fieldMapping != null)
212                {
213                   fieldInfo = fieldMapping.fieldInfo;
214                }
215                else
216                {
217                   String JavaDoc fieldName = Util.xmlNameToFieldName(localName, true);
218                   fieldInfo = FieldInfo.getFieldInfo(o.getClass(), fieldName, true);
219                }
220
221                child = get(o, localName, fieldInfo);
222             }
223
224             if(child == null)
225             {
226                child = newInstance(mapping.cls);
227             }
228
229             if(attrs != null)
230             {
231                for(int i = 0; i < attrs.getLength(); ++i)
232                {
233                   if(attrs.getLocalName(i).length() > 0)
234                   {
235                      if(!attrs.getQName(i).startsWith("xsi:")) //todo horrible
236
{
237                         setAttribute(child, attrs.getLocalName(i), attrs.getValue(i), ctx);
238                      }
239                   }
240                }
241             }
242          }
243          catch(RuntimeException JavaDoc e)
244          {
245             throw e;
246          }
247          catch(Exception JavaDoc e)
248          {
249             throw new NestedRuntimeException("newChild failed for o=" +
250                o +
251                ", uri=" +
252                namespaceURI +
253                ", local="
254                + localName + ", attrs=" + attrs, e
255             );
256          }
257       }
258       else
259       {
260          if(o instanceof Collection JavaDoc)
261          {
262             child = create(namespaceURI, localName, type);
263          }
264          else
265          {
266             Class JavaDoc oCls;
267             if(o instanceof Immutable)
268             {
269                oCls = ((Immutable)o).cls;
270             }
271             else
272             {
273                oCls = o.getClass();
274             }
275
276             String JavaDoc fieldName = Util.xmlNameToFieldName(localName, true);
277             FieldInfo fieldInfo = FieldInfo.getFieldInfo(oCls, fieldName, true);
278             if(Collection JavaDoc.class.isAssignableFrom(fieldInfo.getType()))
279             {
280                child = get(o, localName, fieldInfo);
281
282                // now does this element really represent a Java collection or is it an element that can appear more than once?
283
// try to load the class and create an instance
284
Object JavaDoc item = null;
285                if(type == null || type != null && type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE)
286                {
287                   item = create(namespaceURI, localName, type);
288                }
289
290                if(item != null)
291                {
292                   if(child == null)
293                   {
294                      setChild(new ArrayList JavaDoc(), o, localName);
295                   }
296                   child = item;
297                }
298                else
299                {
300                   if(child == null)
301                   {
302                      child = new ArrayList JavaDoc();
303                   }
304                }
305             }
306             else if(!Util.isAttributeType(fieldInfo.getType()))
307             {
308                // id there is no field mapping
309
ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get(
310                   new ElementToFieldMappingKey(localName, o.getClass())
311                );
312                TypeBinding converter = fieldMapping == null ? null : fieldMapping.converter;
313
314                // if converter != null it will be used in setValue
315
if(converter == null)
316                {
317                   child = newInstance(fieldInfo.getType());
318                }
319             }
320          }
321       }
322
323       return child;
324    }
325
326    public void addChild(Object JavaDoc parent,
327                         Object JavaDoc child,
328                         UnmarshallingContext ctx,
329                         String JavaDoc namespaceURI,
330                         String JavaDoc localName)
331    {
332       if(log.isTraceEnabled())
333       {
334          log.trace("addChild parent=" +
335             parent +
336             " child=" +
337             child +
338             " namespaceURI=" +
339             namespaceURI +
340             " localName=" +
341             localName
342          );
343       }
344
345       if(child instanceof Immutable)
346       {
347          child = ((Immutable)child).newInstance();
348       }
349       setChild(child, parent, localName);
350    }
351
352    public void setValue(Object JavaDoc o, UnmarshallingContext ctx, String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc value)
353    {
354       if(log.isTraceEnabled())
355       {
356          log.trace("setValue object=" +
357             o +
358             " ctx=" +
359             ctx +
360             " namespaceURI=" +
361             namespaceURI +
362             " localName=" +
363             localName +
364             " value=" +
365             value
366          );
367       }
368
369       setAttribute(o, localName, value, ctx);
370    }
371
372    public Object JavaDoc completeRoot(Object JavaDoc root, UnmarshallingContext navigator, String JavaDoc namespaceURI, String JavaDoc localName)
373    {
374       if(log.isTraceEnabled())
375       {
376          log.trace("completeRoot root=" +
377             root +
378             " navigator=" +
379             navigator +
380             " namespaceURI=" +
381             namespaceURI +
382             " localName=" +
383             localName
384          );
385       }
386
387       if(root instanceof Immutable)
388       {
389          root = ((Immutable)root).newInstance();
390       }
391       return root;
392    }
393
394    // Private
395

396    private void addElementToClassMapping(ElementToClassMapping mapping)
397    {
398       elementToClassMapping.put(mapping.element, mapping);
399    }
400
401    private void addElementToFieldMapping(ElementToFieldMapping mapping)
402    {
403       elementToFieldMapping.put(mapping.key, mapping);
404    }
405
406    private void setChild(Object JavaDoc child, Object JavaDoc parent, String JavaDoc localName)
407    {
408       boolean trace = log.isTraceEnabled();
409       Object JavaDoc value = child;
410       if(parent instanceof Collection JavaDoc)
411       {
412          if(trace)
413          {
414             log.trace("Add " + value + " to collection " + parent);
415          }
416          ((Collection JavaDoc)parent).add(value);
417       }
418       else
419       {
420          final ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get(
421             new ElementToFieldMappingKey(localName, parent.getClass())
422          );
423          if(fieldMapping != null)
424          {
425             if(trace)
426             {
427                log.trace("Add " + value + " to " + parent + " using field mapping " + fieldMapping);
428             }
429             set(parent, value, localName, fieldMapping.fieldInfo);
430          }
431          else
432          {
433             Class JavaDoc parentCls = parent instanceof Immutable ?
434                ((Immutable)parent).cls :
435                parent.getClass();
436
437             String JavaDoc fieldName = Util.xmlNameToFieldName(localName, true);
438             FieldInfo fieldInfo = FieldInfo.getFieldInfo(parentCls, fieldName, false);
439             if(trace)
440             {
441                log.trace("Add " + value + " to property " + fieldName + " of " + parentCls);
442             }
443
444             if(fieldInfo != null)
445             {
446                if(!(child instanceof Collection JavaDoc) && Collection JavaDoc.class.isAssignableFrom(fieldInfo.getType()))
447                {
448                   Object JavaDoc o = get(parent, localName, fieldInfo);
449                   Collection JavaDoc col = (Collection JavaDoc)o;
450                   if(trace)
451                   {
452                      log.trace("Add " + value + " to collection " + col + " retrieved from " + fieldName);
453                   }
454                   col.add(child);
455                }
456                else
457                {
458                   set(parent, value, localName, fieldInfo);
459                }
460             }
461          }
462       }
463    }
464
465    private void setAttribute(Object JavaDoc o, String JavaDoc localName, String JavaDoc value, UnmarshallingContext ctx)
466    {
467       if(o instanceof Collection JavaDoc)
468       {
469          XSTypeDefinition type = ctx.getType();
470          if(type == null)
471          {
472             log.warn("Type is not available for collection item " + localName + "=" + value + " -> adding as string.");
473             ((Collection JavaDoc)o).add(value);
474          }
475          else
476          {
477             if(type.getName() == null)
478             {
479                throw new IllegalStateException JavaDoc("Name is null for simple type?!");
480             }
481
482             Object JavaDoc trgValue = SimpleTypeBindings.unmarshal(type.getName(), value, ctx.getNamespaceContext());
483             ((Collection JavaDoc)o).add(trgValue);
484          }
485       }
486       else
487       {
488          Object JavaDoc fieldValue = null;
489          final ElementToFieldMapping fieldMapping = (ElementToFieldMapping)elementToFieldMapping.get(
490             new ElementToFieldMappingKey(localName, o.getClass())
491          );
492
493          if(fieldMapping != null)
494          {
495             fieldValue = fieldMapping.converter.unmarshal(value);
496             set(o, fieldValue, localName, fieldMapping.fieldInfo);
497          }
498          else
499          {
500             Class JavaDoc oCls;
501             if(o instanceof Immutable)
502             {
503                oCls = ((Immutable)o).cls;
504             }
505             else
506             {
507                oCls = o.getClass();
508             }
509
510             final String JavaDoc fieldName = Util.xmlNameToFieldName(localName, true);
511             FieldInfo fieldInfo = FieldInfo.getFieldInfo(oCls, fieldName, true);
512
513             fieldValue = SimpleTypeBindings.unmarshal(value, fieldInfo.getType());
514             set(o, fieldValue, localName, fieldInfo);
515          }
516       }
517    }
518
519    /**
520     * Converts namspace URI and local name into a class name, tries to load the class,
521     * create an instance and return it.
522     *
523     * @param namespaceURI element's namespace URI
524     * @param localName element's local name
525     * @return null if the class could not be loaded, otherwise an instance of the loaded class
526     */

527    private static Object JavaDoc create(String JavaDoc namespaceURI, String JavaDoc localName, XSTypeDefinition type)
528    {
529       Object JavaDoc o = null;
530
531       String JavaDoc clsName = type != null && type.getName() != null ?
532          Util.xmlNameToClassName(namespaceURI, type.getName(), true) :
533          Util.xmlNameToClassName(namespaceURI, localName, true);
534
535       Class JavaDoc cls = null;
536       try
537       {
538          cls = Thread.currentThread().getContextClassLoader().loadClass(clsName);
539       }
540       catch(ClassNotFoundException JavaDoc e)
541       {
542          if(log.isTraceEnabled())
543          {
544             log.trace("create: failed to load class " + clsName);
545          }
546       }
547
548       if(cls != null)
549       {
550          o = newInstance(cls);
551       }
552
553       return o;
554    }
555
556    private static Object JavaDoc get(Object JavaDoc o, String JavaDoc localName, FieldInfo fieldInfo)
557    {
558       if(log.isTraceEnabled())
559       {
560          log.trace("get object=" + o + " localName=" + localName);
561       }
562
563       Object JavaDoc value;
564       if(o instanceof Immutable)
565       {
566          Immutable con = ((Immutable)o);
567          value = con.getChild(localName);
568       }
569       else
570       {
571          value = fieldInfo.getValue(o);
572       }
573       return value;
574    }
575
576    private static void set(Object JavaDoc parent, Object JavaDoc child, String JavaDoc localName, FieldInfo fieldInfo)
577    {
578       if(log.isTraceEnabled())
579       {
580          log.trace("set parent=" + parent + " child=" + child + " localName=" + localName);
581       }
582
583       if(fieldInfo.isWritable())
584       {
585          fieldInfo.setValue(parent, child);
586       }
587       else if(parent instanceof Immutable)
588       {
589          ((Immutable)parent).addChild(localName, child);
590       }
591       else
592       {
593          throw new IllegalStateException JavaDoc("Neither write method nor field were found for " + fieldInfo.getName() +
594             " and the parent object is not an immutable container: parent=" +
595             parent.getClass() +
596             ", localName=" + localName + ", parent=" + parent + ", child=" + child
597          );
598       }
599    }
600
601    private static Object JavaDoc newInstance(Class JavaDoc cls)
602    {
603       if(log.isTraceEnabled())
604       {
605          log.trace("new " + cls.getName());
606       }
607
608       Object JavaDoc instance;
609       try
610       {
611          Constructor JavaDoc ctor = cls.getConstructor(null);
612          instance = ctor.newInstance(null);
613       }
614       catch(NoSuchMethodException JavaDoc e)
615       {
616          log.warn("No no-arg constructor in " + cls);
617          instance = new Immutable(cls);
618       }
619       catch(Exception JavaDoc e)
620       {
621          throw new IllegalStateException JavaDoc("Failed to create an instance of " +
622             cls +
623             " with the no-arg constructor: "
624             + e.getMessage()
625          );
626       }
627       return instance;
628    }
629
630    // Inner classes
631

632    private class ElementToClassMapping
633    {
634       public final String JavaDoc element;
635
636       public final Class JavaDoc cls;
637
638       public ElementToClassMapping(String JavaDoc element, Class JavaDoc cls)
639       {
640          this.element = element;
641          this.cls = cls;
642       }
643
644       public String JavaDoc toString()
645       {
646          StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
647          buffer.append("ElementToClass@").append(System.identityHashCode(this));
648          buffer.append("{element=").append(element);
649          if(cls != null)
650          {
651             buffer.append(" class=").append(cls.getName());
652          }
653          buffer.append("}");
654          return buffer.toString();
655       }
656
657       public boolean equals(Object JavaDoc o)
658       {
659          if(this == o)
660          {
661             return true;
662          }
663          if(!(o instanceof ElementToClassMapping))
664          {
665             return false;
666          }
667
668          final ElementToClassMapping classMapping = (ElementToClassMapping)o;
669
670          if(cls != null ? !cls.equals(classMapping.cls) : classMapping.cls != null)
671          {
672             return false;
673          }
674
675          return true;
676       }
677
678       public int hashCode()
679       {
680          return (cls != null ? cls.hashCode() : 0);
681       }
682    }
683
684    private class ElementToFieldMappingKey
685    {
686       public final String JavaDoc element;
687
688       public final Class JavaDoc cls;
689
690       public ElementToFieldMappingKey(String JavaDoc element, Class JavaDoc cls)
691       {
692          this.element = element;
693          this.cls = cls;
694       }
695
696       public boolean equals(Object JavaDoc o)
697       {
698          if(this == o)
699          {
700             return true;
701          }
702          if(!(o instanceof ElementToFieldMappingKey))
703          {
704             return false;
705          }
706
707          final ElementToFieldMappingKey elementToFieldMappingKey = (ElementToFieldMappingKey)o;
708
709          if(cls != null ? !cls.equals(elementToFieldMappingKey.cls) : elementToFieldMappingKey.cls != null)
710          {
711             return false;
712          }
713          if(element != null ?
714             !element.equals(elementToFieldMappingKey.element) :
715             elementToFieldMappingKey.element != null)
716          {
717             return false;
718          }
719
720          return true;
721       }
722
723       public int hashCode()
724       {
725          int result;
726          result = (element != null ? element.hashCode() : 0);
727          result = 29 * result + (cls != null ? cls.hashCode() : 0);
728          return result;
729       }
730    }
731
732    private class ElementToFieldMapping
733    {
734       public final String JavaDoc element;
735       public final Class JavaDoc cls;
736       public final TypeBinding converter;
737       public final ElementToFieldMappingKey key;
738       public final FieldInfo fieldInfo;
739
740       public ElementToFieldMapping(String JavaDoc element, Class JavaDoc cls, String JavaDoc fieldName, TypeBinding converter)
741       {
742          this.element = element;
743          this.cls = cls;
744          this.converter = converter;
745          key = new ElementToFieldMappingKey(element, cls);
746          fieldInfo = FieldInfo.getFieldInfo(cls, fieldName, true);
747       }
748
749       public String JavaDoc toString()
750       {
751          StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
752          buffer.append("ElementToField@").append(System.identityHashCode(this));
753          buffer.append("{element=").append(element);
754          if(cls != null)
755          {
756             buffer.append(" class=").append(cls.getName());
757          }
758          buffer.append(" field=").append(fieldInfo.getName());
759          if(converter != null)
760          {
761             buffer.append(" convertor=").append(converter.getClass().getName());
762          }
763          buffer.append("}");
764          return buffer.toString();
765       }
766
767       public boolean equals(Object JavaDoc o)
768       {
769          if(this == o)
770          {
771             return true;
772          }
773          if(!(o instanceof ElementToFieldMapping))
774          {
775             return false;
776          }
777
778          final ElementToFieldMapping elementToFieldMapping = (ElementToFieldMapping)o;
779
780          if(cls != null ? !cls.equals(elementToFieldMapping.cls) : elementToFieldMapping.cls != null)
781          {
782             return false;
783          }
784          if(element != null ? !element.equals(elementToFieldMapping.element) : elementToFieldMapping.element != null)
785          {
786             return false;
787          }
788
789          if(!fieldInfo.getName().equals(elementToFieldMapping.fieldInfo.getName()))
790          {
791             return false;
792          }
793
794          return true;
795       }
796
797       public int hashCode()
798       {
799          int result;
800          result = (element != null ? element.hashCode() : 0);
801          result = 29 * result + (cls != null ? cls.hashCode() : 0);
802          result = 29 * result + fieldInfo.getName().hashCode();
803          return result;
804       }
805    }
806 }
807
Popular Tags